mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-28 19:56:50 +00:00
update
This commit is contained in:
parent
26607251d9
commit
90d64d8f51
|
@ -26,7 +26,8 @@
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"no_data": "No data",
|
"no_data": "No data",
|
||||||
"wait": "Wait",
|
"wait": "Wait",
|
||||||
"remove": "Remove"
|
"remove": "Remove",
|
||||||
|
"apply": "Apply"
|
||||||
},
|
},
|
||||||
"more": {
|
"more": {
|
||||||
"_comment": "'More' tab",
|
"_comment": "'More' tab",
|
||||||
|
@ -34,6 +35,9 @@
|
||||||
"about_project": "About us",
|
"about_project": "About us",
|
||||||
"about_app": "About application",
|
"about_app": "About application",
|
||||||
"onboarding": "Onboarding",
|
"onboarding": "Onboarding",
|
||||||
|
"create_ssh_key": "Create ssh key",
|
||||||
|
"generate_key": "Generate key",
|
||||||
|
"generate_key_text": "You can generate ssh key",
|
||||||
"console": "Console",
|
"console": "Console",
|
||||||
"about_app_page": {
|
"about_app_page": {
|
||||||
"text": "Application version v.{}"
|
"text": "Application version v.{}"
|
||||||
|
@ -223,7 +227,8 @@
|
||||||
"createUser": "Create",
|
"createUser": "Create",
|
||||||
"serviceTurnOff": "Turn off",
|
"serviceTurnOff": "Turn off",
|
||||||
"serviceTurnOn": "Turn on",
|
"serviceTurnOn": "Turn on",
|
||||||
"jobAdded": "Job added"
|
"jobAdded": "Job added",
|
||||||
|
"runJobs": "Run jobs"
|
||||||
},
|
},
|
||||||
"validations": {
|
"validations": {
|
||||||
"required": "Required",
|
"required": "Required",
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
"details": "Детальная информация",
|
"details": "Детальная информация",
|
||||||
"no_data": "Нет данных",
|
"no_data": "Нет данных",
|
||||||
"wait": "Ожидайте",
|
"wait": "Ожидайте",
|
||||||
"remove": "Удалить"
|
"remove": "Удалить",
|
||||||
|
"apply": "Подать"
|
||||||
},
|
},
|
||||||
"more": {
|
"more": {
|
||||||
"_comment": "вкладка еще",
|
"_comment": "вкладка еще",
|
||||||
|
@ -35,6 +36,9 @@
|
||||||
"about_app": "О приложении",
|
"about_app": "О приложении",
|
||||||
"onboarding": "Приветствие",
|
"onboarding": "Приветствие",
|
||||||
"console": "Консоль",
|
"console": "Консоль",
|
||||||
|
"create_ssh_key": "Создать ssh ключ",
|
||||||
|
"generate_key": "Сгенерировать ключ",
|
||||||
|
"generate_key_text": "Вы сможете сгенерировать ключ",
|
||||||
"about_app_page": {
|
"about_app_page": {
|
||||||
"text": "Версия приложения: v.{}"
|
"text": "Версия приложения: v.{}"
|
||||||
},
|
},
|
||||||
|
@ -216,7 +220,8 @@
|
||||||
"createUser": "Создать запись",
|
"createUser": "Создать запись",
|
||||||
"serviceTurnOff": "Остановить",
|
"serviceTurnOff": "Остановить",
|
||||||
"serviceTurnOn": "Запустить",
|
"serviceTurnOn": "Запустить",
|
||||||
"jobAdded": "Задача добавленна"
|
"jobAdded": "Задача добавленна",
|
||||||
|
"runJobs": "Запустите задачи"
|
||||||
},
|
},
|
||||||
"validations": {
|
"validations": {
|
||||||
"required": "обязательное поле",
|
"required": "обязательное поле",
|
||||||
|
|
|
@ -44,7 +44,7 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/wakelock/ios"
|
:path: ".symlinks/plugins/wakelock/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||||
|
|
|
@ -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/api_config.dart';
|
||||||
import 'package:selfprivacy/logic/get_it/console.dart';
|
import 'package:selfprivacy/logic/get_it/console.dart';
|
||||||
import 'package:selfprivacy/logic/get_it/navigation.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';
|
import 'package:selfprivacy/logic/get_it/timer.dart';
|
||||||
|
|
||||||
export 'package:selfprivacy/logic/get_it/api_config.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/navigation.dart';
|
||||||
export 'package:selfprivacy/logic/get_it/timer.dart';
|
export 'package:selfprivacy/logic/get_it/timer.dart';
|
||||||
|
|
||||||
|
|
||||||
final getIt = GetIt.instance;
|
final getIt = GetIt.instance;
|
||||||
|
|
||||||
Future<void> getItSetup() async {
|
Future<void> getItSetup() async {
|
||||||
|
@ -17,6 +17,7 @@ Future<void> getItSetup() async {
|
||||||
|
|
||||||
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
||||||
getIt.registerSingleton<TimerModel>(TimerModel());
|
getIt.registerSingleton<TimerModel>(TimerModel());
|
||||||
|
getIt.registerSingleton<SSHModel>(SSHModel()..init());
|
||||||
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
|
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
|
||||||
|
|
||||||
await getIt.allReady();
|
await getIt.allReady();
|
||||||
|
|
|
@ -22,19 +22,21 @@ class HiveConfig {
|
||||||
await Hive.openBox<User>(BNames.users);
|
await Hive.openBox<User>(BNames.users);
|
||||||
await Hive.openBox(BNames.servicesState);
|
await Hive.openBox(BNames.servicesState);
|
||||||
|
|
||||||
var cipher = HiveAesCipher(await getEncriptedKey());
|
var cipher = HiveAesCipher(await getEncriptedKey(BNames.key));
|
||||||
await Hive.openBox(BNames.appConfig, encryptionCipher: cipher);
|
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 {
|
static Future<Uint8List> getEncriptedKey(String encKey) async {
|
||||||
final secureStorage = FlutterSecureStorage();
|
final secureStorage = FlutterSecureStorage();
|
||||||
var hasEncryptionKey = await secureStorage.containsKey(key: BNames.key);
|
var hasEncryptionKey = await secureStorage.containsKey(key: encKey);
|
||||||
if (!hasEncryptionKey) {
|
if (!hasEncryptionKey) {
|
||||||
var key = Hive.generateSecureKey();
|
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!);
|
return base64Url.decode(string!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +51,7 @@ class BNames {
|
||||||
static String servicesState = 'servicesState';
|
static String servicesState = 'servicesState';
|
||||||
|
|
||||||
static String key = 'key';
|
static String key = 'key';
|
||||||
|
static String sshEnckey = 'sshEngkey';
|
||||||
|
|
||||||
static String cloudFlareDomain = 'cloudFlareDomain';
|
static String cloudFlareDomain = 'cloudFlareDomain';
|
||||||
static String hetznerKey = 'hetznerKey';
|
static String hetznerKey = 'hetznerKey';
|
||||||
|
@ -61,4 +64,7 @@ class BNames {
|
||||||
static String isLoading = 'isLoading';
|
static String isLoading = 'isLoading';
|
||||||
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
|
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
|
||||||
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
|
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
|
||||||
|
static String sshConfig = 'sshConfig';
|
||||||
|
static String sshPrivateKey = "sshPrivateKey";
|
||||||
|
static String sshPublicKey = "sshPublicKey";
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ class HetznerApi extends ApiMap {
|
||||||
/// check the branch name, it could be "development" or "master".
|
/// check the branch name, it could be "development" or "master".
|
||||||
|
|
||||||
var data = jsonDecode(
|
var data = jsonDecode(
|
||||||
'''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "ssh_keys":["kherel"], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/development/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"}''');
|
'''{"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(
|
Response serverCreateResponse = await client.post(
|
||||||
'/servers',
|
'/servers',
|
||||||
|
|
|
@ -96,6 +96,15 @@ class ServerApi extends ApiMap {
|
||||||
client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}');
|
client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}');
|
||||||
client.close();
|
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 {
|
extension UrlServerExt on ServiceTypes {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:ionicons/ionicons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:unicons/unicons.dart';
|
|
||||||
|
|
||||||
enum InitializingSteps {
|
enum InitializingSteps {
|
||||||
setHeznerKey,
|
setHeznerKey,
|
||||||
|
@ -86,7 +86,7 @@ extension ServiceTypesExt on ServiceTypes {
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.git:
|
||||||
return BrandIcons.git;
|
return BrandIcons.git;
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.vpn:
|
||||||
return UniconsLine.cloud_lock;
|
return Ionicons.shield_checkmark_outline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/get_it/ssh_helper.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
|
@ -40,7 +41,7 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createOrRemove(ServiceToggleJob job) {
|
void createOrRemoveServiceToggleJob(ServiceToggleJob job) {
|
||||||
var newJobsList = <Job>[];
|
var newJobsList = <Job>[];
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||||
|
@ -53,6 +54,26 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
removeJob(removingJob.id);
|
removeJob(removingJob.id);
|
||||||
} else {
|
} else {
|
||||||
newJobsList.add(job);
|
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));
|
emit(JobsStateWithJobs(newJobsList));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +82,6 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
var jobs = (state as JobsStateWithJobs).jobList;
|
var jobs = (state as JobsStateWithJobs).jobList;
|
||||||
emit(JobsStateLoading());
|
emit(JobsStateLoading());
|
||||||
|
|
||||||
var newUsers = <User>[];
|
var newUsers = <User>[];
|
||||||
for (var job in jobs) {
|
for (var job in jobs) {
|
||||||
if (job is CreateUserJob) {
|
if (job is CreateUserJob) {
|
||||||
|
@ -75,6 +95,10 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
servicesCubit.turnOffList([job.type]);
|
servicesCubit.turnOffList([job.type]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (job is CreateSSHKeyJob) {
|
||||||
|
await getIt<SSHModel>().generateKeys();
|
||||||
|
api.sendSsh(getIt<SSHModel>().savedPubKey!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usersCubit.addUsers(newUsers);
|
usersCubit.addUsers(newUsers);
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
part of 'services_cubit.dart';
|
part of 'services_cubit.dart';
|
||||||
|
|
||||||
const switchableServices = [
|
|
||||||
ServiceTypes.passwordManager,
|
|
||||||
ServiceTypes.cloud,
|
|
||||||
ServiceTypes.socialNetwork,
|
|
||||||
ServiceTypes.git,
|
|
||||||
ServiceTypes.vpn,
|
|
||||||
];
|
|
||||||
|
|
||||||
class ServicesState extends Equatable {
|
class ServicesState extends Equatable {
|
||||||
const ServicesState({
|
const ServicesState({
|
||||||
required this.isPasswordManagerEnable,
|
required this.isPasswordManagerEnable,
|
||||||
|
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,3 +45,10 @@ class ServiceToggleJob extends Job {
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [id, title, type, needToTurnOn];
|
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];
|
||||||
|
}
|
||||||
|
|
|
@ -52,10 +52,16 @@ class BrandText extends StatelessWidget {
|
||||||
type: TextType.onboardingTitle,
|
type: TextType.onboardingTitle,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.h2(String? text, {TextStyle? style}) => BrandText(
|
factory BrandText.h2(
|
||||||
|
String? text, {
|
||||||
|
TextStyle? style,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
}) =>
|
||||||
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.h2,
|
type: TextType.h2,
|
||||||
style: style,
|
style: style,
|
||||||
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ionicons/ionicons.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.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_divider/brand_divider.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.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_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.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/initializing/initializing.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||||
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
||||||
|
@ -21,9 +28,14 @@ class MorePage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var jobsCubit = context.watch<JobsCubit>();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
child: BrandHeader(title: 'basis.more'.tr()),
|
child: BrandHeader(
|
||||||
|
title: 'basis.more'.tr(),
|
||||||
|
hasFlashButton: true,
|
||||||
|
),
|
||||||
preferredSize: Size.fromHeight(52),
|
preferredSize: Size.fromHeight(52),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
|
@ -63,6 +75,25 @@ class MorePage extends StatelessWidget {
|
||||||
iconData: BrandIcons.terminal,
|
iconData: BrandIcons.terminal,
|
||||||
goTo: Console(),
|
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 {
|
class _NavItem extends StatelessWidget {
|
||||||
const _NavItem({
|
const _NavItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -88,7 +186,52 @@ class _NavItem extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => Navigator.of(context).push(materialRoute(goTo)),
|
onTap: () => Navigator.of(context).push(materialRoute(goTo)),
|
||||||
child: Container(
|
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),
|
padding: EdgeInsets.symmetric(vertical: 24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
|
@ -111,7 +254,6 @@ class _NavItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.dart';
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
|
@ -21,6 +23,14 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../rootRoute.dart';
|
import '../rootRoute.dart';
|
||||||
|
|
||||||
|
const switchableServices = [
|
||||||
|
ServiceTypes.passwordManager,
|
||||||
|
ServiceTypes.cloud,
|
||||||
|
ServiceTypes.socialNetwork,
|
||||||
|
ServiceTypes.git,
|
||||||
|
ServiceTypes.vpn,
|
||||||
|
];
|
||||||
|
|
||||||
class ServicesPage extends StatefulWidget {
|
class ServicesPage extends StatefulWidget {
|
||||||
ServicesPage({Key? key}) : super(key: key);
|
ServicesPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@ -72,10 +82,20 @@ class _Card extends StatelessWidget {
|
||||||
|
|
||||||
var serviceState = context.watch<ServicesCubit>().state;
|
var serviceState = context.watch<ServicesCubit>().state;
|
||||||
var jobsCubit = context.watch<JobsCubit>();
|
var jobsCubit = context.watch<JobsCubit>();
|
||||||
var hasSwitcher = switchableServices.contains(serviceType);
|
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(
|
return GestureDetector(
|
||||||
onTap: isReady
|
onTap: isSwithOn
|
||||||
? () => showDialog<void>(
|
? () => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
// isScrollControlled: true,
|
// isScrollControlled: true,
|
||||||
|
@ -84,7 +104,7 @@ class _Card extends StatelessWidget {
|
||||||
return _ServiceDetails(
|
return _ServiceDetails(
|
||||||
serviceType: serviceType,
|
serviceType: serviceType,
|
||||||
status:
|
status:
|
||||||
isReady ? StateType.stable : StateType.uninitialized,
|
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||||
title: serviceType.title,
|
title: serviceType.title,
|
||||||
icon: serviceType.icon,
|
icon: serviceType.icon,
|
||||||
changeTab: changeTab,
|
changeTab: changeTab,
|
||||||
|
@ -99,20 +119,19 @@ class _Card extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
IconStatusMask(
|
IconStatusMask(
|
||||||
status: isReady ? StateType.stable : StateType.uninitialized,
|
status:
|
||||||
|
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||||
child: Icon(serviceType.icon, size: 30, color: Colors.white),
|
child: Icon(serviceType.icon, size: 30, color: Colors.white),
|
||||||
),
|
),
|
||||||
if (hasSwitcher) ...[
|
if (isReady && switchebleService) ...[
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
late bool isActive;
|
late bool isActive;
|
||||||
var jobState = jobsCubit.state;
|
if (hasSwitchJob) {
|
||||||
if (jobState is JobsStateWithJobs &&
|
isActive = ((jobState as JobsStateWithJobs)
|
||||||
jobState.jobList.any((el) =>
|
.jobList
|
||||||
el is ServiceToggleJob &&
|
.firstWhere((el) =>
|
||||||
el.type == serviceType)) {
|
|
||||||
isActive = (jobState.jobList.firstWhere((el) =>
|
|
||||||
el is ServiceToggleJob &&
|
el is ServiceToggleJob &&
|
||||||
el.type == serviceType) as ServiceToggleJob)
|
el.type == serviceType) as ServiceToggleJob)
|
||||||
.needToTurnOn;
|
.needToTurnOn;
|
||||||
|
@ -122,7 +141,8 @@ class _Card extends StatelessWidget {
|
||||||
|
|
||||||
return BrandSwitch(
|
return BrandSwitch(
|
||||||
value: isActive,
|
value: isActive,
|
||||||
onChanged: (value) => jobsCubit.createOrRemove(
|
onChanged: (value) =>
|
||||||
|
jobsCubit.createOrRemoveServiceToggleJob(
|
||||||
ServiceToggleJob(
|
ServiceToggleJob(
|
||||||
type: serviceType,
|
type: serviceType,
|
||||||
needToTurnOn: value,
|
needToTurnOn: value,
|
||||||
|
@ -134,6 +154,12 @@ class _Card extends StatelessWidget {
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
ClipRect(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.h2(serviceType.title),
|
BrandText.h2(serviceType.title),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
|
@ -141,6 +167,27 @@ class _Card extends StatelessWidget {
|
||||||
SizedBox(height: 10),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
58
pubspec.lock
58
pubspec.lock
|
@ -14,7 +14,7 @@ packages:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.1"
|
version: "1.7.2"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -29,13 +29,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
asn1lib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: asn1lib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.1"
|
version: "2.8.1"
|
||||||
basic_utils:
|
basic_utils:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -126,7 +133,7 @@ packages:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.1"
|
||||||
checked_yaml:
|
checked_yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -489,7 +496,7 @@ packages:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.7.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -610,7 +617,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
@ -658,6 +665,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -810,6 +831,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
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:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -851,21 +879,21 @@ packages:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.8"
|
version: "1.17.10"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.4.2"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.19"
|
version: "0.4.0"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -873,6 +901,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
tuple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: tuple
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -880,13 +915,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
unicons:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: unicons
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -31,10 +31,13 @@ dependencies:
|
||||||
pretty_dio_logger: ^1.1.1
|
pretty_dio_logger: ^1.1.1
|
||||||
provider: ^6.0.0
|
provider: ^6.0.0
|
||||||
share_plus: ^2.1.4
|
share_plus: ^2.1.4
|
||||||
unicons: ^2.0.1
|
|
||||||
url_launcher: ^6.0.2
|
url_launcher: ^6.0.2
|
||||||
wakelock: ^0.5.0+2
|
wakelock: ^0.5.0+2
|
||||||
basic_utils: ^3.4.0
|
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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue