mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-17 22:29:15 +00:00
Merge pull request 'services-switchers' (#32) from services-switchers into master
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/32
This commit is contained in:
commit
e064598c73
16
.editorconfig
Normal file
16
.editorconfig
Normal file
|
@ -0,0 +1,16 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.dart]
|
||||
max_line_length = 150
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -26,7 +26,8 @@
|
|||
"details": "Details",
|
||||
"no_data": "No data",
|
||||
"wait": "Wait",
|
||||
"remove": "Remove"
|
||||
"remove": "Remove",
|
||||
"apply": "Apply"
|
||||
},
|
||||
"more": {
|
||||
"_comment": "'More' tab",
|
||||
|
@ -34,6 +35,9 @@
|
|||
"about_project": "About us",
|
||||
"about_app": "About application",
|
||||
"onboarding": "Onboarding",
|
||||
"create_ssh_key": "Create ssh key",
|
||||
"generate_key": "Generate key",
|
||||
"generate_key_text": "You can generate ssh key",
|
||||
"console": "Console",
|
||||
"about_app_page": {
|
||||
"text": "Application version v.{}"
|
||||
|
@ -147,6 +151,13 @@
|
|||
"bottom_sheet": {
|
||||
"1": "You can connect and create a new user here:"
|
||||
}
|
||||
},
|
||||
"vpn": {
|
||||
"title": "VPN Server",
|
||||
"subtitle": "Private VPN server",
|
||||
"bottom_sheet": {
|
||||
"1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure"
|
||||
}
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -212,7 +223,12 @@
|
|||
"_comment": "Jobs list",
|
||||
"title": "Jobs list",
|
||||
"start": "Start",
|
||||
"empty": "No jobs"
|
||||
"empty": "No jobs",
|
||||
"createUser": "Create",
|
||||
"serviceTurnOff": "Turn off",
|
||||
"serviceTurnOn": "Turn on",
|
||||
"jobAdded": "Job added",
|
||||
"runJobs": "Run jobs"
|
||||
},
|
||||
"validations": {
|
||||
"required": "Required",
|
||||
|
@ -222,4 +238,4 @@
|
|||
"length": "Length is [] shoud be {}",
|
||||
"user_alredy_exist": "Already exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"details": "Детальная информация",
|
||||
"no_data": "Нет данных",
|
||||
"wait": "Ожидайте",
|
||||
"remove": "Удалить"
|
||||
"remove": "Удалить",
|
||||
"apply": "Подать"
|
||||
},
|
||||
"more": {
|
||||
"_comment": "вкладка еще",
|
||||
|
@ -35,6 +36,9 @@
|
|||
"about_app": "О приложении",
|
||||
"onboarding": "Приветствие",
|
||||
"console": "Консоль",
|
||||
"create_ssh_key": "Создать ssh ключ",
|
||||
"generate_key": "Сгенерировать ключ",
|
||||
"generate_key_text": "Вы сможете сгенерировать ключ",
|
||||
"about_app_page": {
|
||||
"text": "Версия приложения: v.{}"
|
||||
},
|
||||
|
@ -212,7 +216,12 @@
|
|||
"_comment": "Jobs list",
|
||||
"title": "Задачи",
|
||||
"start": "Начать выполенение",
|
||||
"empty": "Пусто"
|
||||
"empty": "Пусто",
|
||||
"createUser": "Создать запись",
|
||||
"serviceTurnOff": "Остановить",
|
||||
"serviceTurnOn": "Запустить",
|
||||
"jobAdded": "Задача добавленна",
|
||||
"runJobs": "Запустите задачи"
|
||||
},
|
||||
"validations": {
|
||||
"required": "обязательное поле",
|
||||
|
@ -222,4 +231,4 @@
|
|||
"length": "Длина строки [] должна быть {}",
|
||||
"user_alredy_exist": "Имя уже используется"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -44,7 +44,7 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
|
|
BIN
ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph
Normal file
BIN
ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ios/build/Pods.build/Release-iphonesimulator/wakelock.build/dgph
Normal file
BIN
ios/build/Pods.build/Release-iphonesimulator/wakelock.build/dgph
Normal file
Binary file not shown.
|
@ -4,6 +4,7 @@ import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.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/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
|
||||
class BlocAndProviderConfig extends StatelessWidget {
|
||||
|
@ -13,11 +14,9 @@ class BlocAndProviderConfig extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// var platformBrightness =
|
||||
// SchedulerBinding.instance.window.platformBrightness;
|
||||
// var isDark = platformBrightness == Brightness.dark;
|
||||
var isDark = false;
|
||||
var usersCubit = UsersCubit();
|
||||
var servicesCubit = ServicesCubit();
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
|
@ -28,11 +27,17 @@ class BlocAndProviderConfig extends StatelessWidget {
|
|||
),
|
||||
BlocProvider(
|
||||
lazy: false,
|
||||
create: (_) => AppConfigCubit()..load(),
|
||||
create: (_) => AppConfigCubit(servicesCubit)..load(),
|
||||
),
|
||||
BlocProvider(create: (_) => ProvidersCubit()),
|
||||
BlocProvider(create: (_) => usersCubit..load(), lazy: false),
|
||||
BlocProvider(create: (_) => JobsCubit(usersCubit)),
|
||||
BlocProvider(create: (_) => servicesCubit..load(), lazy: false),
|
||||
BlocProvider(
|
||||
create: (_) => JobsCubit(
|
||||
usersCubit: usersCubit,
|
||||
servicesCubit: servicesCubit,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
|
|
|
@ -2,6 +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/timer.dart';
|
||||
|
||||
export 'package:selfprivacy/logic/get_it/api_config.dart';
|
||||
|
@ -9,7 +10,6 @@ export 'package:selfprivacy/logic/get_it/console.dart';
|
|||
export 'package:selfprivacy/logic/get_it/navigation.dart';
|
||||
export 'package:selfprivacy/logic/get_it/timer.dart';
|
||||
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
Future<void> getItSetup() async {
|
||||
|
@ -17,6 +17,7 @@ Future<void> getItSetup() async {
|
|||
|
||||
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
||||
getIt.registerSingleton<TimerModel>(TimerModel());
|
||||
getIt.registerSingleton<SSHModel>(SSHModel()..init());
|
||||
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
|
||||
|
||||
await getIt.allReady();
|
||||
|
|
|
@ -20,22 +20,23 @@ class HiveConfig {
|
|||
|
||||
await Hive.openBox(BNames.appSettings);
|
||||
await Hive.openBox<User>(BNames.users);
|
||||
await Hive.openBox(BNames.servicesState);
|
||||
|
||||
var cipher = HiveAesCipher(await getEncriptedKey());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static Future<Uint8List> getEncriptedKey() async {
|
||||
final FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
||||
var containsEncryptionKey =
|
||||
await secureStorage.containsKey(key: BNames.key);
|
||||
if (!containsEncryptionKey) {
|
||||
static Future<Uint8List> getEncriptedKey(String encKey) async {
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
var hasEncryptionKey = await secureStorage.containsKey(key: encKey);
|
||||
if (!hasEncryptionKey) {
|
||||
var key = Hive.generateSecureKey();
|
||||
await secureStorage.write(key: BNames.key, value: base64UrlEncode(key));
|
||||
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
|
||||
}
|
||||
|
||||
String? string = await secureStorage.read(key: BNames.key);
|
||||
String? string = await secureStorage.read(key: encKey);
|
||||
return base64Url.decode(string!);
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +48,10 @@ class BNames {
|
|||
static String users = 'users';
|
||||
|
||||
static String appSettings = 'appSettings';
|
||||
static String servicesState = 'servicesState';
|
||||
|
||||
static String key = 'key';
|
||||
static String sshEnckey = 'sshEngkey';
|
||||
|
||||
static String cloudFlareDomain = 'cloudFlareDomain';
|
||||
static String hetznerKey = 'hetznerKey';
|
||||
|
@ -61,4 +64,7 @@ class BNames {
|
|||
static String isLoading = 'isLoading';
|
||||
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
|
||||
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
|
||||
static String sshConfig = 'sshConfig';
|
||||
static String sshPrivateKey = "sshPrivateKey";
|
||||
static String sshPublicKey = "sshPublicKey";
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
|||
import 'package:selfprivacy/logic/models/hetzner_server_info.dart';
|
||||
import 'package:selfprivacy/logic/models/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:selfprivacy/utils/password_generator2.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
class HetznerApi extends ApiMap {
|
||||
bool hasLoger;
|
||||
|
@ -73,30 +73,28 @@ class HetznerApi extends ApiMap {
|
|||
required User rootUser,
|
||||
required String domainName,
|
||||
}) async {
|
||||
var dbPassword = getRandomString(40);
|
||||
|
||||
const chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
|
||||
var dbStorageName = getRandomString(6, chars);
|
||||
var client = await getClient();
|
||||
|
||||
Response dbCreateResponse = await client.post(
|
||||
'/volumes',
|
||||
data: {
|
||||
"size": 10,
|
||||
"name": dbStorageName,
|
||||
"name": StringGenerators.dbStorageName(),
|
||||
"labels": {"labelkey": "value"},
|
||||
"location": "fsn1",
|
||||
"automount": false,
|
||||
"format": "ext4"
|
||||
},
|
||||
);
|
||||
|
||||
var dbPassword = StringGenerators.dbPassword();
|
||||
var dbId = dbCreateResponse.data['volume']['id'];
|
||||
|
||||
/// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
|
||||
/// check the branch name, it could be "development" or "master".
|
||||
|
||||
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-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"}'''
|
||||
);
|
||||
'''{"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} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''');
|
||||
|
||||
Response serverCreateResponse = await client.post(
|
||||
'/servers',
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
|
||||
import 'api_map.dart';
|
||||
|
@ -89,4 +90,44 @@ class ServerApi extends ApiMap {
|
|||
close(client);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> switchService(ServiceTypes type, bool needToTurnOn) async {
|
||||
var client = await getClient();
|
||||
client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}');
|
||||
client.close();
|
||||
}
|
||||
|
||||
Future<void> sendSsh(String ssh) async {
|
||||
var client = await getClient();
|
||||
client.post(
|
||||
'/services/ssh/enable',
|
||||
data: {"public_key": ssh},
|
||||
);
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
extension UrlServerExt on ServiceTypes {
|
||||
String get url {
|
||||
switch (this) {
|
||||
// case ServiceTypes.mail:
|
||||
// return ''; // cannot be swithch off
|
||||
// case ServiceTypes.messenger:
|
||||
// return ''; // external service
|
||||
// case ServiceTypes.video:
|
||||
// return ''; // jeetsu meet not working
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'bitwarden';
|
||||
case ServiceTypes.cloud:
|
||||
return 'nextcloud';
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'pleroma';
|
||||
case ServiceTypes.git:
|
||||
return 'gitea';
|
||||
case ServiceTypes.vpn:
|
||||
return 'ocserv';
|
||||
default:
|
||||
throw Exception('wrong state');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
|
||||
enum InitializingSteps {
|
||||
setHeznerKey,
|
||||
setCloudFlareKey,
|
||||
|
@ -10,3 +15,80 @@ enum InitializingSteps {
|
|||
}
|
||||
enum Period { hour, day, month }
|
||||
|
||||
enum ServiceTypes {
|
||||
mail,
|
||||
messenger,
|
||||
passwordManager,
|
||||
video,
|
||||
cloud,
|
||||
socialNetwork,
|
||||
git,
|
||||
vpn,
|
||||
}
|
||||
|
||||
extension ServiceTypesExt on ServiceTypes {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return 'services.mail.title'.tr();
|
||||
case ServiceTypes.messenger:
|
||||
return 'services.messenger.title'.tr();
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'services.password_manager.title'.tr();
|
||||
case ServiceTypes.video:
|
||||
return 'services.video.title'.tr();
|
||||
case ServiceTypes.cloud:
|
||||
return 'services.cloud.title'.tr();
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'services.social_network.title'.tr();
|
||||
case ServiceTypes.git:
|
||||
return 'services.git.title'.tr();
|
||||
case ServiceTypes.vpn:
|
||||
return 'services.vpn.title'.tr();
|
||||
}
|
||||
}
|
||||
|
||||
String get subtitle {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return 'services.mail.subtitle'.tr();
|
||||
case ServiceTypes.messenger:
|
||||
return 'services.messenger.subtitle'.tr();
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'services.password_manager.subtitle'.tr();
|
||||
case ServiceTypes.video:
|
||||
return 'services.video.subtitle'.tr();
|
||||
case ServiceTypes.cloud:
|
||||
return 'services.cloud.subtitle'.tr();
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'services.social_network.subtitle'.tr();
|
||||
case ServiceTypes.git:
|
||||
return 'services.git.subtitle'.tr();
|
||||
case ServiceTypes.vpn:
|
||||
return 'services.vpn.subtitle'.tr();
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return BrandIcons.envelope;
|
||||
case ServiceTypes.messenger:
|
||||
return BrandIcons.messanger;
|
||||
case ServiceTypes.passwordManager:
|
||||
return BrandIcons.key;
|
||||
case ServiceTypes.video:
|
||||
return BrandIcons.webcam;
|
||||
case ServiceTypes.cloud:
|
||||
return BrandIcons.upload;
|
||||
case ServiceTypes.socialNetwork:
|
||||
return BrandIcons.social;
|
||||
case ServiceTypes.git:
|
||||
return BrandIcons.git;
|
||||
case ServiceTypes.vpn:
|
||||
return Ionicons.shield_checkmark_outline;
|
||||
}
|
||||
}
|
||||
|
||||
String get txt => this.toString().split('.')[1];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||
|
||||
|
@ -43,9 +44,10 @@ part 'app_config_state.dart';
|
|||
/// c. if server is okay set that fully checked
|
||||
|
||||
class AppConfigCubit extends Cubit<AppConfigState> {
|
||||
AppConfigCubit() : super(InitialAppConfigState());
|
||||
AppConfigCubit(this.servicesCubit) : super(InitialAppConfigState());
|
||||
|
||||
final repository = AppConfigRepository();
|
||||
final ServicesCubit servicesCubit;
|
||||
|
||||
Future<void> load() async {
|
||||
var state = await repository.load();
|
||||
|
@ -232,6 +234,7 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
|||
|
||||
if (isServerWorking) {
|
||||
await repository.saveHasFinalChecked(true);
|
||||
servicesCubit.allOn();
|
||||
|
||||
emit(state.copyWith(
|
||||
hasFinalChecked: true,
|
||||
|
@ -259,12 +262,16 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
|||
|
||||
void clearAppConfig() {
|
||||
closeTimer();
|
||||
servicesCubit.allOff();
|
||||
|
||||
repository.clearAppConfig();
|
||||
emit(InitialAppConfigState());
|
||||
}
|
||||
|
||||
Future<void> serverDelete() async {
|
||||
closeTimer();
|
||||
servicesCubit.allOff();
|
||||
|
||||
if (state.hetznerServer != null) {
|
||||
await repository.deleteServer(state.cloudFlareDomain!);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import 'dart:async';
|
|||
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/jobs/job.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
class UserFormCubit extends FormCubit {
|
||||
UserFormCubit({
|
||||
|
@ -32,7 +32,7 @@ class UserFormCubit extends FormCubit {
|
|||
);
|
||||
|
||||
password = FieldCubit(
|
||||
initalValue: isEdit ? user!.password : genPass(),
|
||||
initalValue: isEdit ? user!.password : StringGenerators.userPassword(),
|
||||
validations: [
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>((s) => passwordRegExp.hasMatch(s),
|
||||
|
@ -56,7 +56,7 @@ class UserFormCubit extends FormCubit {
|
|||
late FieldCubit<String> password;
|
||||
|
||||
void genNewPassword() {
|
||||
password.externalSetValue(genPass());
|
||||
password.externalSetValue(StringGenerators.userPassword());
|
||||
}
|
||||
|
||||
final JobsCubit jobsCubit;
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.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/models/jobs/job.dart';
|
||||
import 'package:selfprivacy/logic/get_it/ssh_helper.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
export 'package:provider/provider.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
part 'jobs_state.dart';
|
||||
|
||||
class JobsCubit extends Cubit<JobsState> {
|
||||
JobsCubit(this.usersCubit) : super(JobsStateEmpty());
|
||||
JobsCubit({
|
||||
required this.usersCubit,
|
||||
required this.servicesCubit,
|
||||
}) : super(JobsStateEmpty());
|
||||
|
||||
final api = ServerApi();
|
||||
final UsersCubit usersCubit;
|
||||
final ServicesCubit servicesCubit;
|
||||
|
||||
void addJob(Job job) {
|
||||
var newJobsList = <Job>[];
|
||||
|
@ -21,6 +29,10 @@ class JobsCubit extends Cubit<JobsState> {
|
|||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar(SnackBar(
|
||||
content: Text('jobs.jobAdded'.tr()),
|
||||
duration: const Duration(seconds: 2),
|
||||
));
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
|
||||
|
@ -29,16 +41,63 @@ class JobsCubit extends Cubit<JobsState> {
|
|||
emit(newState);
|
||||
}
|
||||
|
||||
void createOrRemoveServiceToggleJob(ServiceToggleJob job) {
|
||||
var newJobsList = <Job>[];
|
||||
if (state is JobsStateWithJobs) {
|
||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
var needToRemoveJob =
|
||||
newJobsList.any((el) => el is ServiceToggleJob && el.type == job.type);
|
||||
if (needToRemoveJob) {
|
||||
var removingJob = newJobsList
|
||||
.firstWhere(((el) => el is ServiceToggleJob && el.type == job.type));
|
||||
removeJob(removingJob.id);
|
||||
} else {
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar(SnackBar(
|
||||
content: Text('jobs.jobAdded'.tr()),
|
||||
duration: const Duration(seconds: 2),
|
||||
));
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
}
|
||||
|
||||
void createShhJobIfNotExist(CreateSSHKeyJob job) {
|
||||
var newJobsList = <Job>[];
|
||||
if (state is JobsStateWithJobs) {
|
||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
var isExistInJobList = newJobsList.any((el) => el is CreateSSHKeyJob);
|
||||
if (!isExistInJobList) {
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar(SnackBar(
|
||||
content: Text('jobs.jobAdded'.tr()),
|
||||
duration: const Duration(seconds: 2),
|
||||
));
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> applyAll() async {
|
||||
if (state is JobsStateWithJobs) {
|
||||
var jobs = (state as JobsStateWithJobs).jobList;
|
||||
emit(JobsStateLoading());
|
||||
|
||||
var newUsers = <User>[];
|
||||
for (var job in jobs) {
|
||||
if (job is CreateUserJob) {
|
||||
newUsers.add(job.user);
|
||||
await api.createUser(job.user);
|
||||
} else if (job is ServiceToggleJob) {
|
||||
await api.switchService(job.type, job.needToTurnOn);
|
||||
if (job.needToTurnOn) {
|
||||
servicesCubit.turnOnist([job.type]);
|
||||
} else {
|
||||
servicesCubit.turnOffList([job.type]);
|
||||
}
|
||||
}
|
||||
if (job is CreateSSHKeyJob) {
|
||||
await getIt<SSHModel>().generateKeys();
|
||||
api.sendSsh(getIt<SSHModel>().savedPubKey!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
63
lib/logic/cubit/services/services_cubit.dart
Normal file
63
lib/logic/cubit/services/services_cubit.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
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/common_enum/common_enum.dart';
|
||||
|
||||
part 'services_state.dart';
|
||||
|
||||
class ServicesCubit extends Cubit<ServicesState> {
|
||||
ServicesCubit() : super(ServicesState.allOff());
|
||||
|
||||
Box box = Hive.box(BNames.servicesState);
|
||||
|
||||
void load() {
|
||||
emit(
|
||||
ServicesState(
|
||||
isPasswordManagerEnable:
|
||||
box.get(ServiceTypes.passwordManager.txt, defaultValue: false),
|
||||
isCloudEnable: box.get(ServiceTypes.cloud.txt, defaultValue: false),
|
||||
isGitEnable: box.get(ServiceTypes.git.txt, defaultValue: false),
|
||||
isSocialNetworkEnable:
|
||||
box.get(ServiceTypes.socialNetwork.txt, defaultValue: false),
|
||||
isVpnEnable: box.get(ServiceTypes.vpn.txt, defaultValue: false),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void allOn() {
|
||||
box.put(ServiceTypes.passwordManager.txt, true);
|
||||
box.put(ServiceTypes.cloud.txt, true);
|
||||
box.put(ServiceTypes.git.txt, true);
|
||||
box.put(ServiceTypes.socialNetwork.txt, true);
|
||||
box.put(ServiceTypes.vpn.txt, true);
|
||||
|
||||
emit(ServicesState.allOn());
|
||||
}
|
||||
|
||||
void allOff() {
|
||||
box.put(ServiceTypes.passwordManager.txt, false);
|
||||
box.put(ServiceTypes.cloud.txt, false);
|
||||
box.put(ServiceTypes.git.txt, false);
|
||||
box.put(ServiceTypes.socialNetwork.txt, false);
|
||||
box.put(ServiceTypes.vpn.txt, false);
|
||||
|
||||
emit(ServicesState.allOff());
|
||||
}
|
||||
|
||||
void turnOffList(List<ServiceTypes> list) {
|
||||
for (final service in list) {
|
||||
box.put(service.txt, false);
|
||||
}
|
||||
|
||||
emit(state.disableList(list));
|
||||
}
|
||||
|
||||
void turnOnist(List<ServiceTypes> list) {
|
||||
for (final service in list) {
|
||||
box.put(service.txt, true);
|
||||
}
|
||||
|
||||
emit(state.enableList(list));
|
||||
}
|
||||
}
|
93
lib/logic/cubit/services/services_state.dart
Normal file
93
lib/logic/cubit/services/services_state.dart
Normal file
|
@ -0,0 +1,93 @@
|
|||
part of 'services_cubit.dart';
|
||||
|
||||
class ServicesState extends Equatable {
|
||||
const ServicesState({
|
||||
required this.isPasswordManagerEnable,
|
||||
required this.isCloudEnable,
|
||||
required this.isGitEnable,
|
||||
required this.isSocialNetworkEnable,
|
||||
required this.isVpnEnable,
|
||||
});
|
||||
|
||||
final bool isPasswordManagerEnable;
|
||||
final bool isCloudEnable;
|
||||
final bool isGitEnable;
|
||||
final bool isSocialNetworkEnable;
|
||||
final bool isVpnEnable;
|
||||
|
||||
factory ServicesState.allOff() => ServicesState(
|
||||
isPasswordManagerEnable: false,
|
||||
isCloudEnable: false,
|
||||
isGitEnable: false,
|
||||
isSocialNetworkEnable: false,
|
||||
isVpnEnable: false,
|
||||
);
|
||||
factory ServicesState.allOn() => ServicesState(
|
||||
isPasswordManagerEnable: true,
|
||||
isCloudEnable: true,
|
||||
isGitEnable: true,
|
||||
isSocialNetworkEnable: true,
|
||||
isVpnEnable: true,
|
||||
);
|
||||
|
||||
ServicesState enableList(
|
||||
List<ServiceTypes> list,
|
||||
) =>
|
||||
ServicesState(
|
||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||
? true
|
||||
: isPasswordManagerEnable,
|
||||
isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable,
|
||||
isGitEnable:
|
||||
list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable,
|
||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
||||
? true
|
||||
: isPasswordManagerEnable,
|
||||
isVpnEnable:
|
||||
list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable,
|
||||
);
|
||||
|
||||
ServicesState disableList(
|
||||
List<ServiceTypes> list,
|
||||
) =>
|
||||
ServicesState(
|
||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||
? false
|
||||
: isPasswordManagerEnable,
|
||||
isCloudEnable:
|
||||
list.contains(ServiceTypes.cloud) ? false : isCloudEnable,
|
||||
isGitEnable:
|
||||
list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable,
|
||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
||||
? false
|
||||
: isPasswordManagerEnable,
|
||||
isVpnEnable:
|
||||
list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
isPasswordManagerEnable,
|
||||
isCloudEnable,
|
||||
isGitEnable,
|
||||
isSocialNetworkEnable,
|
||||
isVpnEnable
|
||||
];
|
||||
|
||||
bool isEnableByType(ServiceTypes type) {
|
||||
switch (type) {
|
||||
case ServiceTypes.passwordManager:
|
||||
return isPasswordManagerEnable;
|
||||
case ServiceTypes.cloud:
|
||||
return isCloudEnable;
|
||||
case ServiceTypes.socialNetwork:
|
||||
return isSocialNetworkEnable;
|
||||
case ServiceTypes.git:
|
||||
return isGitEnable;
|
||||
case ServiceTypes.vpn:
|
||||
return isVpnEnable;
|
||||
default:
|
||||
throw Exception('wrong state');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,21 +34,3 @@ class UsersCubit extends Cubit<UsersState> {
|
|||
emit(UsersState(users));
|
||||
}
|
||||
}
|
||||
|
||||
// final initMockUsers = <User>[
|
||||
// User(login: 'Heartbreaking.Goose', password: genPass()),
|
||||
// User(login: 'Alma.lawson', password: genPass()),
|
||||
// User(login: 'Bee.gees', password: genPass()),
|
||||
// User(login: 'Bim.jennings', password: genPass()),
|
||||
// User(login: 'Debra.holt', password: genPass()),
|
||||
// User(login: 'Georgia.young', password: genPass()),
|
||||
// User(login: 'Kenzi.lawson', password: genPass()),
|
||||
// User(login: 'Le.jennings', password: genPass()),
|
||||
// User(login: 'Kirill.Zh', password: genPass()),
|
||||
// User(login: 'Tina.Bolton', password: genPass()),
|
||||
// User(login: 'Rebekah.Lynn', password: genPass()),
|
||||
// User(login: 'Aleena.Armstrong', password: genPass()),
|
||||
// User(login: 'Rosemary.Williams', password: genPass()),
|
||||
// User(login: 'Sullivan.Nixon', password: genPass()),
|
||||
// User(login: 'Aleena.Armstrong', password: genPass()),
|
||||
// ];
|
||||
|
|
|
@ -2,7 +2,10 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class NavigationService {
|
||||
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
|
||||
GlobalKey<ScaffoldMessengerState>();
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
NavigatorState? get navigator => navigatorKey.currentState;
|
||||
|
||||
void showPopUpDialog(AlertDialog dialog) {
|
||||
|
@ -13,4 +16,10 @@ class NavigationService {
|
|||
builder: (_) => dialog,
|
||||
);
|
||||
}
|
||||
|
||||
void showSnackBar(SnackBar snackBar) {
|
||||
final state = scaffoldMessengerKey.currentState!;
|
||||
|
||||
state.showSnackBar(snackBar);
|
||||
}
|
||||
}
|
||||
|
|
33
lib/logic/get_it/ssh_helper.dart
Normal file
33
lib/logic/get_it/ssh_helper.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pointycastle/pointycastle.dart';
|
||||
import 'package:rsa_encrypt/rsa_encrypt.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:pointycastle/api.dart' as crypto;
|
||||
import 'package:ssh_key/ssh_key.dart' as ssh_key;
|
||||
|
||||
class SSHModel {
|
||||
Box _box = Hive.box(BNames.sshConfig);
|
||||
String? savedPrivateKey;
|
||||
String? savedPubKey;
|
||||
|
||||
Future<void> generateKeys() async {
|
||||
var helper = RsaKeyHelper();
|
||||
crypto.AsymmetricKeyPair pair =
|
||||
await helper.computeRSAKeyPair(helper.getSecureRandom());
|
||||
var privateKey = pair.privateKey as RSAPrivateKey;
|
||||
var publicKey = pair.publicKey as RSAPublicKey;
|
||||
|
||||
savedPrivateKey = helper.encodePrivateKeyToPemPKCS1(privateKey);
|
||||
savedPubKey = publicKey.encode(ssh_key.PubKeyEncoding.openSsh);
|
||||
|
||||
await _box.put(BNames.sshPrivateKey, savedPrivateKey);
|
||||
await _box.put(BNames.sshPublicKey, savedPubKey);
|
||||
}
|
||||
|
||||
void init() {
|
||||
savedPrivateKey = _box.get(BNames.sshPrivateKey);
|
||||
savedPubKey = _box.get(BNames.sshPublicKey);
|
||||
}
|
||||
}
|
54
lib/logic/models/job.dart
Normal file
54
lib/logic/models/job.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
import 'user.dart';
|
||||
|
||||
@immutable
|
||||
class Job extends Equatable {
|
||||
Job({
|
||||
String? id,
|
||||
required this.title,
|
||||
}) : id = id ?? StringGenerators.simpleId();
|
||||
|
||||
final String title;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
||||
|
||||
class CreateUserJob extends Job {
|
||||
CreateUserJob({
|
||||
required this.user,
|
||||
}) : super(title: '${"jobs.createUser".tr()} ${user.login}');
|
||||
|
||||
final User user;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title, user];
|
||||
}
|
||||
|
||||
class ServiceToggleJob extends Job {
|
||||
ServiceToggleJob({
|
||||
required this.type,
|
||||
required this.needToTurnOn,
|
||||
}) : super(
|
||||
title:
|
||||
'${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}');
|
||||
|
||||
final ServiceTypes type;
|
||||
final bool needToTurnOn;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title, type, needToTurnOn];
|
||||
}
|
||||
|
||||
class CreateSSHKeyJob extends Job {
|
||||
CreateSSHKeyJob() : super(title: '${"more.create_ssh_key".tr()}');
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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,
|
||||
required this.title,
|
||||
}) : id = id ?? getRandomString(5);
|
||||
|
||||
final String title;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
||||
|
||||
class CreateUserJob extends Job {
|
||||
CreateUserJob({
|
||||
required this.user,
|
||||
}) : super(title: 'Create ${user.login}');
|
||||
|
||||
final User user;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:crypt/crypt.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/utils/color_utils.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
|
@ -25,7 +26,10 @@ class User extends Equatable {
|
|||
|
||||
Color get color => stringToColor(login);
|
||||
|
||||
Crypt get hashPassword => Crypt.sha512(password);
|
||||
Crypt get hashPassword => Crypt.sha512(
|
||||
password,
|
||||
salt: StringGenerators.passwordSalt(),
|
||||
);
|
||||
|
||||
String toString() {
|
||||
return login;
|
||||
|
|
|
@ -16,11 +16,11 @@ import 'config/localization.dart';
|
|||
import 'logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await HiveConfig.init();
|
||||
Bloc.observer = SimpleBlocObserver();
|
||||
Wakelock.enable();
|
||||
await getItSetup();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
runApp(MyApp());
|
||||
|
@ -37,6 +37,8 @@ class MyApp extends StatelessWidget {
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
|
||||
child: MaterialApp(
|
||||
scaffoldMessengerKey:
|
||||
getIt.get<NavigationService>().scaffoldMessengerKey,
|
||||
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
supportedLocales: context.supportedLocales,
|
||||
|
|
24
lib/ui/components/brand_switch/brand_switch.dart
Normal file
24
lib/ui/components/brand_switch/brand_switch.dart
Normal file
|
@ -0,0 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
|
||||
class BrandSwitch extends StatelessWidget {
|
||||
const BrandSwitch({
|
||||
Key? key,
|
||||
required this.onChanged,
|
||||
required this.value,
|
||||
}) : super(key: key);
|
||||
|
||||
final ValueChanged<bool> onChanged;
|
||||
final bool value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Switch(
|
||||
activeColor: BrandColors.green1,
|
||||
activeTrackColor: BrandColors.green2,
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -52,10 +52,16 @@ class BrandText extends StatelessWidget {
|
|||
type: TextType.onboardingTitle,
|
||||
style: style,
|
||||
);
|
||||
factory BrandText.h2(String? text, {TextStyle? style}) => BrandText(
|
||||
factory BrandText.h2(
|
||||
String? text, {
|
||||
TextStyle? style,
|
||||
TextAlign? textAlign,
|
||||
}) =>
|
||||
BrandText(
|
||||
text,
|
||||
type: TextType.h2,
|
||||
style: style,
|
||||
textAlign: textAlign,
|
||||
);
|
||||
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
||||
BrandText(
|
||||
|
|
|
@ -36,11 +36,7 @@ class JobsContent extends StatelessWidget {
|
|||
children: [
|
||||
Expanded(
|
||||
child: BrandCards.small(
|
||||
child: Row(
|
||||
children: [
|
||||
BrandText.body1(j.title),
|
||||
],
|
||||
),
|
||||
child: Text(j.title),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
|
|
|
@ -7,6 +7,7 @@ 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_divider/brand_divider.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
@ -52,9 +53,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Switch(
|
||||
activeColor: BrandColors.green1,
|
||||
activeTrackColor: BrandColors.green2,
|
||||
BrandSwitch(
|
||||
value: Theme.of(context).brightness == Brightness.dark,
|
||||
onChanged: (value) => context
|
||||
.read<AppSettingsCubit>()
|
||||
|
@ -120,69 +119,72 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
|
||||
)),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _TextColumn(
|
||||
title: 'more.settings.5'.tr(),
|
||||
value: 'more.settings.6'.tr(),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: BrandColors.red1,
|
||||
),
|
||||
child: Text(
|
||||
'basis.delete'.tr(),
|
||||
style: TextStyle(
|
||||
color: BrandColors.white,
|
||||
fontWeight: NamedFontWeight.demiBold,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BrandAlert(
|
||||
title: 'modals.3'.tr(),
|
||||
contentText: 'modals.6'.tr(),
|
||||
acitons: [
|
||||
ActionButton(
|
||||
text: 'modals.7'.tr(),
|
||||
isRed: true,
|
||||
onPressed: () async {
|
||||
await context
|
||||
.read<AppConfigCubit>()
|
||||
.serverDelete();
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
ActionButton(
|
||||
text: 'basis.cancel'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
// deleteServer(context)
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget deleteServer(BuildContext context) {
|
||||
// todo: need to check
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
|
||||
)),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _TextColumn(
|
||||
title: 'more.settings.5'.tr(),
|
||||
value: 'more.settings.6'.tr(),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: BrandColors.red1,
|
||||
),
|
||||
child: Text(
|
||||
'basis.delete'.tr(),
|
||||
style: TextStyle(
|
||||
color: BrandColors.white,
|
||||
fontWeight: NamedFontWeight.demiBold,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BrandAlert(
|
||||
title: 'modals.3'.tr(),
|
||||
contentText: 'modals.6'.tr(),
|
||||
acitons: [
|
||||
ActionButton(
|
||||
text: 'modals.7'.tr(),
|
||||
isRed: true,
|
||||
onPressed: () async {
|
||||
await context.read<AppConfigCubit>().serverDelete();
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
ActionButton(
|
||||
text: 'basis.cancel'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TextColumn extends StatelessWidget {
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/job.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_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_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
|
||||
import 'package:selfprivacy/ui/pages/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
||||
|
@ -21,9 +28,14 @@ class MorePage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var jobsCubit = context.watch<JobsCubit>();
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: BrandHeader(title: 'basis.more'.tr()),
|
||||
child: BrandHeader(
|
||||
title: 'basis.more'.tr(),
|
||||
hasFlashButton: true,
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
|
@ -63,6 +75,25 @@ class MorePage extends StatelessWidget {
|
|||
iconData: BrandIcons.terminal,
|
||||
goTo: Console(),
|
||||
),
|
||||
_MoreMenuTapItem(
|
||||
title: 'more.create_ssh_key'.tr(),
|
||||
iconData: Ionicons.key_outline,
|
||||
onTap: () {
|
||||
showDialog<void>(
|
||||
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(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -72,6 +103,73 @@ class MorePage extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _MoreDetails extends StatelessWidget {
|
||||
const _MoreDetails({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
required this.text,
|
||||
}) : super(key: key);
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final Function onTap;
|
||||
final String text;
|
||||
@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.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: paddingH15V30,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: StateType.stable,
|
||||
child: Icon(icon, size: 40, color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(title),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
text,
|
||||
style: textStyle,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
Center(
|
||||
child: Container(
|
||||
child: BrandButton.rised(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onTap();
|
||||
},
|
||||
text: 'more.generate_key'.tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NavItem extends StatelessWidget {
|
||||
const _NavItem({
|
||||
Key? key,
|
||||
|
@ -88,29 +186,73 @@ class _NavItem extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(materialRoute(goTo)),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1.0,
|
||||
color: BrandColors.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
BrandText.body1(title),
|
||||
Spacer(),
|
||||
SizedBox(
|
||||
width: 56,
|
||||
child: Icon(
|
||||
iconData,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _MoreMenuItem(
|
||||
iconData: iconData,
|
||||
title: title,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoreMenuTapItem extends StatelessWidget {
|
||||
const _MoreMenuTapItem({
|
||||
Key? key,
|
||||
required this.iconData,
|
||||
required this.onTap,
|
||||
required this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
final IconData iconData;
|
||||
final Function onTap;
|
||||
final String title;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap();
|
||||
},
|
||||
child: _MoreMenuItem(
|
||||
iconData: iconData,
|
||||
title: title,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoreMenuItem extends StatelessWidget {
|
||||
const _MoreMenuItem({
|
||||
Key? key,
|
||||
required this.iconData,
|
||||
required this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
final IconData iconData;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1.0,
|
||||
color: BrandColors.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
BrandText.body1(title),
|
||||
Spacer(),
|
||||
SizedBox(
|
||||
width: 56,
|
||||
child: Icon(
|
||||
iconData,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class CpuChart extends StatelessWidget {
|
|||
interval: 20,
|
||||
rotateAngle: 90.0,
|
||||
showTitles: true,
|
||||
getTextStyles: (value) => const TextStyle(
|
||||
getTextStyles: (_, __) => const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.purple,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -60,7 +60,7 @@ class CpuChart extends StatelessWidget {
|
|||
return bottomTitle(value.toInt());
|
||||
}),
|
||||
leftTitles: SideTitles(
|
||||
getTextStyles: (value) => progressTextStyleLight.copyWith(
|
||||
getTextStyles: (_, __) => progressTextStyleLight.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.gray4
|
||||
: null,
|
||||
|
|
|
@ -71,7 +71,7 @@ class NetworkChart extends StatelessWidget {
|
|||
interval: 20,
|
||||
rotateAngle: 90.0,
|
||||
showTitles: true,
|
||||
getTextStyles: (value) => const TextStyle(
|
||||
getTextStyles: (_, __) => const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.purple,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -87,7 +87,7 @@ class NetworkChart extends StatelessWidget {
|
|||
].reduce(max) *
|
||||
1.2 /
|
||||
10,
|
||||
getTextStyles: (value) => progressTextStyleLight.copyWith(
|
||||
getTextStyles: (_, __) => progressTextStyleLight.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.gray4
|
||||
: null,
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.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/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/job.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_cards/brand_cards.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_switch/brand_switch.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';
|
||||
|
@ -17,6 +23,14 @@ import 'package:url_launcher/url_launcher.dart';
|
|||
|
||||
import '../rootRoute.dart';
|
||||
|
||||
const switchableServices = [
|
||||
ServiceTypes.passwordManager,
|
||||
ServiceTypes.cloud,
|
||||
ServiceTypes.socialNetwork,
|
||||
ServiceTypes.git,
|
||||
ServiceTypes.vpn,
|
||||
];
|
||||
|
||||
class ServicesPage extends StatefulWidget {
|
||||
ServicesPage({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -63,52 +77,25 @@ class _Card extends StatelessWidget {
|
|||
final ServiceTypes serviceType;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String title;
|
||||
IconData iconData;
|
||||
String subtitle;
|
||||
|
||||
switch (serviceType) {
|
||||
case ServiceTypes.mail:
|
||||
iconData = BrandIcons.envelope;
|
||||
title = 'services.mail.title'.tr();
|
||||
subtitle = 'services.mail.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.messenger:
|
||||
iconData = BrandIcons.messanger;
|
||||
title = 'services.messenger.title'.tr();
|
||||
subtitle = 'services.messenger.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.passwordManager:
|
||||
iconData = BrandIcons.key;
|
||||
title = 'services.password_manager.title'.tr();
|
||||
subtitle = 'services.password_manager.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.video:
|
||||
iconData = BrandIcons.webcam;
|
||||
title = 'services.video.title'.tr();
|
||||
subtitle = 'services.video.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.cloud:
|
||||
iconData = BrandIcons.upload;
|
||||
title = 'services.cloud.title'.tr();
|
||||
subtitle = 'services.cloud.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.socialNetwork:
|
||||
iconData = BrandIcons.social;
|
||||
title = 'services.social_network.title'.tr();
|
||||
subtitle = 'services.social_network.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.git:
|
||||
iconData = BrandIcons.git;
|
||||
title = 'services.git.title'.tr();
|
||||
subtitle = 'services.git.subtitle'.tr();
|
||||
break;
|
||||
}
|
||||
|
||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
var changeTab = context.read<ChangeTab>().onPress;
|
||||
|
||||
var serviceState = context.watch<ServicesCubit>().state;
|
||||
var jobsCubit = context.watch<JobsCubit>();
|
||||
var jobState = jobsCubit.state;
|
||||
|
||||
var switchebleService = switchableServices.contains(serviceType);
|
||||
var hasSwitchJob = switchebleService &&
|
||||
jobState is JobsStateWithJobs &&
|
||||
jobState.jobList
|
||||
.any((el) => el is ServiceToggleJob && el.type == serviceType);
|
||||
|
||||
var isSwithOn = isReady &&
|
||||
(!switchableServices.contains(serviceType) ||
|
||||
serviceState.isEnableByType(serviceType));
|
||||
|
||||
return GestureDetector(
|
||||
onTap: isReady
|
||||
onTap: isSwithOn
|
||||
? () => showDialog<void>(
|
||||
context: context,
|
||||
// isScrollControlled: true,
|
||||
|
@ -117,9 +104,9 @@ class _Card extends StatelessWidget {
|
|||
return _ServiceDetails(
|
||||
serviceType: serviceType,
|
||||
status:
|
||||
isReady ? StateType.stable : StateType.uninitialized,
|
||||
title: title,
|
||||
icon: iconData,
|
||||
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||
title: serviceType.title,
|
||||
icon: serviceType.icon,
|
||||
changeTab: changeTab,
|
||||
);
|
||||
},
|
||||
|
@ -129,15 +116,76 @@ class _Card extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: isReady ? StateType.stable : StateType.uninitialized,
|
||||
child: Icon(iconData, size: 30, color: Colors.white),
|
||||
Row(
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status:
|
||||
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||
child: Icon(serviceType.icon, size: 30, color: Colors.white),
|
||||
),
|
||||
if (isReady && switchebleService) ...[
|
||||
Spacer(),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
late bool isActive;
|
||||
if (hasSwitchJob) {
|
||||
isActive = ((jobState as JobsStateWithJobs)
|
||||
.jobList
|
||||
.firstWhere((el) =>
|
||||
el is ServiceToggleJob &&
|
||||
el.type == serviceType) as ServiceToggleJob)
|
||||
.needToTurnOn;
|
||||
} else {
|
||||
isActive = serviceState.isEnableByType(serviceType);
|
||||
}
|
||||
|
||||
return BrandSwitch(
|
||||
value: isActive,
|
||||
onChanged: (value) =>
|
||||
jobsCubit.createOrRemoveServiceToggleJob(
|
||||
ServiceToggleJob(
|
||||
type: serviceType,
|
||||
needToTurnOn: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(title),
|
||||
SizedBox(height: 10),
|
||||
BrandText.body2(subtitle),
|
||||
SizedBox(height: 10),
|
||||
ClipRect(
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(serviceType.title),
|
||||
SizedBox(height: 10),
|
||||
BrandText.body2(serviceType.subtitle),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
if (hasSwitchJob)
|
||||
Positioned(
|
||||
bottom: 30,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: 3,
|
||||
sigmaY: 2,
|
||||
),
|
||||
child: BrandText.h2(
|
||||
'jobs.runJobs'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -145,16 +193,6 @@ class _Card extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
enum ServiceTypes {
|
||||
mail,
|
||||
messenger,
|
||||
passwordManager,
|
||||
video,
|
||||
cloud,
|
||||
socialNetwork,
|
||||
git,
|
||||
}
|
||||
|
||||
class _ServiceDetails extends StatelessWidget {
|
||||
const _ServiceDetails({
|
||||
Key? key,
|
||||
|
@ -353,7 +391,12 @@ class _ServiceDetails extends StatelessWidget {
|
|||
],
|
||||
));
|
||||
break;
|
||||
case ServiceTypes.vpn:
|
||||
child = Text(
|
||||
'services.vpn.bottom_sheet.1'.tr(),
|
||||
);
|
||||
}
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
|
@ -413,252 +456,3 @@ class _ServiceDetails extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// class _ServiceDetails extends StatelessWidget {
|
||||
// const _ServiceDetails({
|
||||
// Key? key,
|
||||
// required this.serviceType,
|
||||
// required this.icon,
|
||||
// required this.status,
|
||||
// required this.title,
|
||||
// required this.changeTab,
|
||||
// }) : super(key: key);
|
||||
|
||||
// final ServiceTypes serviceType;
|
||||
// final IconData icon;
|
||||
// final StateType status;
|
||||
// final String title;
|
||||
// final ValueChanged<int> changeTab;
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// late Widget child;
|
||||
|
||||
// var config = context.watch<AppConfigCubit>().state;
|
||||
// var domainName = UiHelpers.getDomainName(config);
|
||||
|
||||
// var linksStyle = body1Style.copyWith(
|
||||
// fontSize: 15,
|
||||
// color: Theme.of(context).brightness == Brightness.dark
|
||||
// ? Colors.white
|
||||
// : BrandColors.black,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// decoration: TextDecoration.underline,
|
||||
// // height: 1.1,
|
||||
// );
|
||||
|
||||
// var textStyle = body1Style.copyWith(
|
||||
// color: Theme.of(context).brightness == Brightness.dark
|
||||
// ? Colors.white
|
||||
// : BrandColors.black,
|
||||
// );
|
||||
// switch (serviceType) {
|
||||
// case ServiceTypes.mail:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// child: Text(
|
||||
// 'services.mail.bottom_sheet.2'.tr(),
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// onTap: () {
|
||||
// Navigator.of(context).pop();
|
||||
// changeTab(2);
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.messenger:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.messenger.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// )
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.passwordManager:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.password_manager.bottom_sheet.1'
|
||||
// .tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://password.$domainName'),
|
||||
// child: Text(
|
||||
// 'password.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.video:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.video.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://meet.$domainName'),
|
||||
// child: Text(
|
||||
// 'meet.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.cloud:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://cloud.$domainName'),
|
||||
// child: Text(
|
||||
// 'cloud.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.socialNetwork:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.social_network.bottom_sheet.1'
|
||||
// .tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://social.$domainName'),
|
||||
// child: Text(
|
||||
// 'social.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.git:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.git.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://git.$domainName'),
|
||||
// child: Text(
|
||||
// 'git.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// }
|
||||
// return BrandModalSheet(
|
||||
// child: Navigator(
|
||||
// key: navigatorKey,
|
||||
// initialRoute: '/',
|
||||
// onGenerateRoute: (_) {
|
||||
// return materialRoute(
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: brandPagePadding1,
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// SizedBox(height: 13),
|
||||
// IconStatusMask(
|
||||
// status: status,
|
||||
// child: Icon(icon, size: 40, color: Colors.white),
|
||||
// ),
|
||||
// SizedBox(height: 10),
|
||||
// BrandText.h1(title),
|
||||
// SizedBox(height: 10),
|
||||
// child,
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void _launchURL(url) async {
|
||||
// var _possible = await canLaunch(url);
|
||||
|
||||
// if (_possible) {
|
||||
// try {
|
||||
// await launch(
|
||||
// url,
|
||||
// forceSafariVC: true,
|
||||
// enableJavaScript: true,
|
||||
// );
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
// } else {
|
||||
// throw 'Could not launch $url';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ 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/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';
|
||||
|
|
|
@ -1,138 +1,99 @@
|
|||
import 'dart:math';
|
||||
|
||||
var generator = PasswordGenerator();
|
||||
Random _rnd = Random();
|
||||
|
||||
String genPass() {
|
||||
generator.generate(8);
|
||||
return generator.getGeneratedValue();
|
||||
}
|
||||
|
||||
///Generates a password.
|
||||
///
|
||||
///The password [_generatedValue] is of a specified length, including letters [_letterGen] of mixed cases,
|
||||
///numbers [_numGen], and symbols[_symGen] depending on user choice.
|
||||
class PasswordGenerator {
|
||||
late bool _letterGen;
|
||||
late bool _numGen;
|
||||
late bool _symGen;
|
||||
late String _generatedValue;
|
||||
|
||||
///Constructor.
|
||||
///
|
||||
///[_letterGen] is true to make password generation possible from the opening of the application, and
|
||||
///[_generatedValue] is intialized to the value below so the text containing it can be first generated
|
||||
///upon users request
|
||||
PasswordGenerator() {
|
||||
_letterGen = true;
|
||||
_numGen = true;
|
||||
_symGen = false;
|
||||
_generatedValue = "Press Generate";
|
||||
}
|
||||
|
||||
///Call to generate a value, of [n] length
|
||||
void generate(int n) {
|
||||
//Discards the old value
|
||||
_generatedValue = "";
|
||||
|
||||
///Cannot generate a value without any character types selected
|
||||
if (!_letterGen && !_numGen && !_symGen) {
|
||||
_generatedValue = "No character type selected";
|
||||
return;
|
||||
}
|
||||
|
||||
///'Randomly' selectes caracter type to generate and append [toAppend] to [_generatedValue]
|
||||
// ignore: unnecessary_statements
|
||||
for (n; n > 0; n--) {
|
||||
String? toAppend;
|
||||
var random = new Random();
|
||||
|
||||
///loops until a valid character is generated, meaning the user has to check the character value
|
||||
///to be generated. 'Randomly' picks a character type.
|
||||
while (toAppend == null) {
|
||||
int selector = random.nextInt(3);
|
||||
|
||||
if (selector == 0) {
|
||||
toAppend = _generateLetter();
|
||||
} else if (selector == 1) {
|
||||
toAppend = _generateNumber();
|
||||
} else {
|
||||
toAppend = _generateSymbol();
|
||||
}
|
||||
}
|
||||
|
||||
_generatedValue += toAppend;
|
||||
toAppend = null;
|
||||
}
|
||||
}
|
||||
|
||||
///Generates a letter when called.
|
||||
String _generateLetter() {
|
||||
if (!_letterGen) return '';
|
||||
|
||||
///Finds the integer value for the range between a-z and A-Z, with [base] UTF-16 value for lowercase letters and
|
||||
///[baseUpper] UTF-16 value for uppercase letters
|
||||
int base = "a".codeUnitAt(0);
|
||||
int baseUpper = "A".codeUnitAt(0);
|
||||
int maxRand = ("z".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly selects between upper and lower case generation, randomly generates value from [maxRand], then adding base,
|
||||
///which creates a UTF-16 encoded character to be converted into a string of one character between a-z/A-Z.
|
||||
///This string is then returned.
|
||||
if (random.nextInt(2) == 0) {
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
} else {
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + baseUpper]);
|
||||
}
|
||||
}
|
||||
|
||||
///Generates a number when called
|
||||
String? _generateNumber() {
|
||||
if (!_numGen) return null;
|
||||
|
||||
///Finds the integer value for the range between 0-9
|
||||
int base = "0".codeUnitAt(0);
|
||||
int maxRand = ("9".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be converted into a
|
||||
///string of one character between 0-9. This string is then returned.
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
}
|
||||
|
||||
///Generates a symbol when called
|
||||
String? _generateSymbol() {
|
||||
if (!_symGen) return null;
|
||||
|
||||
///Finds the integer value for the range between symbols !-.
|
||||
|
||||
///(note) which includes symbols !"#$%&'()*+,=.
|
||||
int base = "!".codeUnitAt(0);
|
||||
int maxRand = (".".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be
|
||||
///converted into a string of one character between !-. . This string is then returned.
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
}
|
||||
|
||||
///Toggles letter generation
|
||||
void checkLetterGen(bool value) {
|
||||
_letterGen = value;
|
||||
}
|
||||
|
||||
///Toggles number generation
|
||||
void checkNumGen(bool value) {
|
||||
_numGen = value;
|
||||
}
|
||||
|
||||
///Toggles symbol generation
|
||||
void checkSymGen(bool value) {
|
||||
_symGen = value;
|
||||
}
|
||||
|
||||
///Returns the generated value to be used by generator app
|
||||
String getGeneratedValue() {
|
||||
return _generatedValue;
|
||||
}
|
||||
typedef StringGeneratorFunction = String Function();
|
||||
|
||||
class StringGenerators {
|
||||
static const letters = 'abcdefghijklmnopqrstuvwxyz';
|
||||
static const numbers = '1234567890';
|
||||
static const symbols = '_';
|
||||
|
||||
static String getRandomString(
|
||||
int length, {
|
||||
hasLowercaseLetters = false,
|
||||
hasUppercaseLetters = false,
|
||||
hasNumbers = false,
|
||||
hasSymbols = false,
|
||||
isStrict = false,
|
||||
}) {
|
||||
var chars = '';
|
||||
if (hasLowercaseLetters) chars += letters;
|
||||
if (hasUppercaseLetters) chars += letters.toUpperCase();
|
||||
if (hasNumbers) chars += numbers;
|
||||
if (hasSymbols) chars += symbols;
|
||||
|
||||
assert(chars.isNotEmpty, 'chart empty');
|
||||
|
||||
if (!isStrict) {
|
||||
return genString(length, chars);
|
||||
}
|
||||
|
||||
var res = '';
|
||||
var loose = length;
|
||||
if (hasLowercaseLetters) {
|
||||
loose -= 1;
|
||||
res += genString(1, letters);
|
||||
}
|
||||
if (hasUppercaseLetters) {
|
||||
loose -= 1;
|
||||
res += genString(1, letters.toUpperCase());
|
||||
}
|
||||
if (hasNumbers) {
|
||||
loose -= 1;
|
||||
res += genString(1, numbers.toUpperCase());
|
||||
}
|
||||
if (hasSymbols) {
|
||||
loose -= 1;
|
||||
res += genString(1, symbols);
|
||||
}
|
||||
res += genString(loose, chars);
|
||||
|
||||
var shuffledlist = res.split('')..shuffle();
|
||||
return shuffledlist.join();
|
||||
}
|
||||
|
||||
static String genString(int length, String chars) {
|
||||
return String.fromCharCodes(
|
||||
Iterable.generate(
|
||||
length,
|
||||
(_) => chars.codeUnitAt(
|
||||
_rnd.nextInt(chars.length),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static StringGeneratorFunction userPassword = () => getRandomString(
|
||||
8,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
isStrict: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction passwordSalt = () => getRandomString(
|
||||
8,
|
||||
hasLowercaseLetters: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction simpleId = () => getRandomString(
|
||||
5,
|
||||
hasLowercaseLetters: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction dbPassword = () => getRandomString(
|
||||
40,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
hasSymbols: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction dbStorageName = () => getRandomString(
|
||||
6,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import 'dart:math';
|
||||
|
||||
const _chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_';
|
||||
Random _rnd = Random();
|
||||
|
||||
String getRandomString(int length, [chars = _chars]) => String.fromCharCodes(
|
||||
Iterable.generate(
|
||||
length,
|
||||
(_) => chars.codeUnitAt(
|
||||
_rnd.nextInt(chars.length),
|
||||
),
|
||||
),
|
||||
);
|
181
pubspec.lock
181
pubspec.lock
|
@ -14,7 +14,7 @@ packages:
|
|||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.1"
|
||||
version: "1.7.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -28,28 +28,35 @@ packages:
|
|||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.2.0"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: asn1lib
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
version: "2.8.1"
|
||||
basic_utils:
|
||||
dependency: "direct dev"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: basic_utils
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.5.0"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.1.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -63,42 +70,42 @@ packages:
|
|||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.1.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.7"
|
||||
version: "1.0.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.10"
|
||||
version: "3.0.0"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.12.2"
|
||||
version: "2.1.1"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.12"
|
||||
version: "7.1.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -112,7 +119,7 @@ packages:
|
|||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.1.0"
|
||||
version: "8.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -126,7 +133,7 @@ packages:
|
|||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -140,7 +147,7 @@ packages:
|
|||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.3.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -154,7 +161,7 @@ packages:
|
|||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
version: "4.1.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -168,7 +175,7 @@ packages:
|
|||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -196,7 +203,7 @@ packages:
|
|||
name: cubit_form
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.16"
|
||||
version: "1.0.18"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -210,7 +217,7 @@ packages:
|
|||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.3"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -259,7 +266,7 @@ packages:
|
|||
name: extended_masked_text
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.3.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -294,7 +301,7 @@ packages:
|
|||
name: fl_chart
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.35.0"
|
||||
version: "0.40.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -306,14 +313,14 @@ packages:
|
|||
name: flutter_bloc
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
version: "7.2.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.0"
|
||||
version: "0.9.2"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -325,14 +332,14 @@ packages:
|
|||
name: flutter_markdown
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.2"
|
||||
version: "0.6.5"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_secure_storage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -343,13 +350,20 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: get_it
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "7.2.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -363,7 +377,7 @@ packages:
|
|||
name: graphs
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "2.0.0"
|
||||
hive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -426,7 +440,7 @@ packages:
|
|||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.3"
|
||||
ionicons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -454,7 +468,7 @@ packages:
|
|||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.3"
|
||||
version: "4.1.4"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -482,7 +496,7 @@ packages:
|
|||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.7.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -552,14 +566,14 @@ packages:
|
|||
name: path_provider_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
path_provider_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -573,7 +587,7 @@ packages:
|
|||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.3"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -594,21 +608,21 @@ packages:
|
|||
name: platform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pointycastle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.3.2"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -629,14 +643,14 @@ packages:
|
|||
name: process
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
version: "4.2.3"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "6.0.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -651,6 +665,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
rsa_encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rsa_encrypt
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -664,7 +692,7 @@ packages:
|
|||
name: share_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
share_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -699,21 +727,21 @@ packages:
|
|||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.0.7"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -727,21 +755,21 @@ packages:
|
|||
name: shared_preferences_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.4"
|
||||
version: "1.2.0"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -755,7 +783,7 @@ packages:
|
|||
name: shelf_static
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -774,14 +802,14 @@ packages:
|
|||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.0.3"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.2.1"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -803,6 +831,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
ssh_key:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ssh_key
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -844,21 +879,21 @@ packages:
|
|||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.8"
|
||||
version: "1.17.10"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.4.2"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.19"
|
||||
version: "0.4.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -866,6 +901,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tuple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -873,55 +915,48 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
unicons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: unicons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.6"
|
||||
version: "6.0.9"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.4"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -942,35 +977,35 @@ packages:
|
|||
name: wakelock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
version: "0.5.3+3"
|
||||
wakelock_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0+1"
|
||||
version: "0.1.0+2"
|
||||
wakelock_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
version: "0.2.1+2"
|
||||
wakelock_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0+1"
|
||||
version: "0.2.0+2"
|
||||
wakelock_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
version: "0.1.0+1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -998,7 +1033,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.2.7"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1021,5 +1056,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.13.0 <3.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
dart: ">=2.13.4 <3.0.0"
|
||||
flutter: ">=2.2.3"
|
||||
|
|
24
pubspec.yaml
24
pubspec.yaml
|
@ -4,8 +4,8 @@ publish_to: 'none'
|
|||
version: 0.1.3+5
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
sdk: '>=2.13.4 <3.0.0'
|
||||
flutter: ">=2.2.3"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -16,31 +16,33 @@ dependencies:
|
|||
dio: ^4.0.0-beta7
|
||||
easy_localization: ^3.0.0
|
||||
either_option: ^2.0.1-dev.1
|
||||
equatable: ^2.0.0
|
||||
fl_chart: ^0.35.0
|
||||
flutter_bloc: ^7.0.0
|
||||
equatable: ^2.0.3
|
||||
fl_chart: ^0.40.0
|
||||
flutter_bloc: ^7.1.0
|
||||
flutter_markdown: ^0.6.0
|
||||
flutter_secure_storage: ^4.1.0
|
||||
get_it: ^6.0.0
|
||||
get_it: ^7.2.0
|
||||
hive: ^2.0.0
|
||||
hive_flutter: ^1.0.0
|
||||
ionicons: ^0.1.2
|
||||
json_annotation: ^4.0.0
|
||||
modal_bottom_sheet: ^2.0.0
|
||||
nanoid: ^1.0.0
|
||||
package_info: ^2.0.0
|
||||
pretty_dio_logger: ^1.1.1
|
||||
provider: ^5.0.0
|
||||
provider: ^6.0.0
|
||||
share_plus: ^2.1.4
|
||||
unicons: ^1.0.2
|
||||
url_launcher: ^6.0.2
|
||||
wakelock: ^0.5.0+2
|
||||
basic_utils: ^3.4.0
|
||||
ionicons: ^0.1.2
|
||||
pointycastle: ^3.3.2
|
||||
rsa_encrypt: ^2.0.0
|
||||
ssh_key: ^0.7.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
basic_utils: ^3.0.0-nullsafety.1
|
||||
build_runner: ^1.11.5
|
||||
build_runner: ^2.1.1
|
||||
flutter_launcher_icons: ^0.9.0
|
||||
hive_generator: ^1.0.0
|
||||
json_serializable: ^4.0.2
|
||||
|
|
|
@ -1,30 +1,122 @@
|
|||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:selfprivacy/main.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
group('StringGenerators', () {
|
||||
group('Basic', () {
|
||||
test('assert chart empty', () {
|
||||
expect(() {
|
||||
StringGenerators.getRandomString(8);
|
||||
}, throwsAssertionError);
|
||||
});
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
test('only lowercase string', () {
|
||||
var length = 8;
|
||||
var generatedString =
|
||||
StringGenerators.getRandomString(length, hasLowercaseLetters: true);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, isNot(matches(regExpNumbers)));
|
||||
expect(generatedString, isNot(matches(regExpUppercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpSymbols)));
|
||||
expect(generatedString.length, equals(length));
|
||||
expect(generatedString, matches(regExpLowercaseLetters));
|
||||
});
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
test('only uppercase string', () {
|
||||
var length = 8;
|
||||
var generatedString = StringGenerators.getRandomString(length,
|
||||
hasLowercaseLetters: false, hasUppercaseLetters: true);
|
||||
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, isNot(matches(regExpNumbers)));
|
||||
expect(generatedString, isNot(matches(regExpLowercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpSymbols)));
|
||||
expect(generatedString.length, equals(length));
|
||||
expect(generatedString, matches(regExpUppercaseLetters));
|
||||
});
|
||||
|
||||
test('only numbers string', () {
|
||||
var length = 8;
|
||||
var generatedString = StringGenerators.getRandomString(length,
|
||||
hasLowercaseLetters: false,
|
||||
hasUppercaseLetters: false,
|
||||
hasNumbers: true);
|
||||
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, isNot(matches(regExpUppercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpLowercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpSymbols)));
|
||||
expect(generatedString.length, equals(length));
|
||||
expect(generatedString, matches(regExpNumbers));
|
||||
});
|
||||
|
||||
test('only symbols string', () {
|
||||
var length = 8;
|
||||
var generatedString = StringGenerators.getRandomString(
|
||||
length,
|
||||
hasLowercaseLetters: false,
|
||||
hasUppercaseLetters: false,
|
||||
hasNumbers: false,
|
||||
hasSymbols: true,
|
||||
);
|
||||
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, isNot(matches(regExpUppercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpLowercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpNumbers)));
|
||||
expect(generatedString.length, equals(length));
|
||||
expect(generatedString, matches(regExpSymbols));
|
||||
});
|
||||
});
|
||||
|
||||
group('Strict mode', () {
|
||||
test('All', () {
|
||||
var length = 5;
|
||||
var generatedString = StringGenerators.getRandomString(length,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
hasSymbols: true,
|
||||
isStrict: true);
|
||||
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, matches(regExpLowercaseLetters));
|
||||
expect(generatedString, matches(regExpUppercaseLetters));
|
||||
expect(generatedString, matches(regExpNumbers));
|
||||
expect(generatedString, matches(regExpSymbols));
|
||||
expect(generatedString.length, equals(length));
|
||||
});
|
||||
test('Lowercase letters and numbers', () {
|
||||
var length = 3;
|
||||
var generatedString = StringGenerators.getRandomString(length,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: false,
|
||||
hasNumbers: true,
|
||||
hasSymbols: false,
|
||||
isStrict: true);
|
||||
|
||||
expect(generatedString, isNot(matches(regExpNewLines)));
|
||||
expect(generatedString, isNot(matches(regExpWhiteSpaces)));
|
||||
expect(generatedString, isNot(matches(regExpUppercaseLetters)));
|
||||
expect(generatedString, isNot(matches(regExpSymbols)));
|
||||
expect(generatedString, matches(regExpLowercaseLetters));
|
||||
expect(generatedString, matches(regExpNumbers));
|
||||
expect(generatedString.length, equals(length));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var regExpNewLines = RegExp(r"[\n\r]+");
|
||||
var regExpWhiteSpaces = RegExp(r"[\s]+");
|
||||
var regExpUppercaseLetters = RegExp(r"[A-Z]");
|
||||
var regExpLowercaseLetters = RegExp(r"[a-z]");
|
||||
var regExpNumbers = RegExp(r"[0-9]");
|
||||
var regExpSymbols = RegExp(r'(?:_|[^\w\s])+');
|
||||
|
|
Loading…
Reference in a new issue