forked from SelfPrivacy/selfprivacy.org.app
Merge pull request 'Major Translation improvements and stability fixes' (#8) from master into fdroid
Reviewed-on: kherel/selfprivacy.org.app#8fdroid
commit
82d1c3a651
|
@ -20,18 +20,28 @@
|
||||||
"domain": "Domain",
|
"domain": "Domain",
|
||||||
"saving": "Saving..",
|
"saving": "Saving..",
|
||||||
"nickname": "nickname",
|
"nickname": "nickname",
|
||||||
"loading": "loading"
|
"loading": "Loading...",
|
||||||
|
"later": "I will setup it later",
|
||||||
|
"reset": "Reset",
|
||||||
|
"details": "Details",
|
||||||
|
"no_data": "No data"
|
||||||
},
|
},
|
||||||
"more": {
|
"more": {
|
||||||
"_comment": "'More' tab",
|
"_comment": "'More' tab",
|
||||||
"configuration_wizard": "Setup wizard",
|
"configuration_wizard": "Setup wizard",
|
||||||
"settings": "Application settings",
|
|
||||||
"about_project": "About us",
|
"about_project": "About us",
|
||||||
"about_app": "About application",
|
"about_app": "About application",
|
||||||
"onboarding": "Onboarding",
|
"onboarding": "Onboarding",
|
||||||
"console": "Console",
|
"console": "Console",
|
||||||
"about_app_page": {
|
"about_app_page": {
|
||||||
"text": "Тут любая служебная информация, v.{}"
|
"text": "Тут любая служебная информация, v.{}"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Application settings",
|
||||||
|
"1": "Dark Theme",
|
||||||
|
"2": "Change your the app theme",
|
||||||
|
"3": "Reset app config",
|
||||||
|
"4": "Reset api keys and root user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"onboarding": {
|
"onboarding": {
|
||||||
|
@ -46,26 +56,27 @@
|
||||||
"page_title": "Your Data Center",
|
"page_title": "Your Data Center",
|
||||||
"server": {
|
"server": {
|
||||||
"card_title": "Server",
|
"card_title": "Server",
|
||||||
|
"status": "Status — Good",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "It's a virtual computer, where all your services live.",
|
"1": "It's a virtual computer, where all your services live.",
|
||||||
"2": "1 CPU, RAM 4Gb, 40Gb — $5 per month",
|
"2": "General information",
|
||||||
"3": "Status — Good"
|
"3": "Location"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
"card_title": "Domain",
|
"card_title": "Domain",
|
||||||
|
"status": "Status — Good",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "It's your personal internet address that will point to the server and other services of yours.",
|
"1": "It's your personal internet address that will point to the server and other services of yours.",
|
||||||
"2": "{} — expires on {}",
|
"2": "{} — expires on {}"
|
||||||
"3": "Status — Good"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"backup": {
|
"backup": {
|
||||||
"card_title": "Backup",
|
"card_title": "Backup",
|
||||||
|
"status": "Status — Good",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "Will save your day in case of incident: hackers attack, server deletion, etc.",
|
"1": "Will save your day in case of incident: hackers attack, server deletion, etc.",
|
||||||
"2": "3Gb/10Gb, last backup was yesterday {}",
|
"2": "3Gb/10Gb, last backup was yesterday {}"
|
||||||
"3": "Status — Good"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -122,7 +133,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"git": {
|
"git": {
|
||||||
"title": "Git-server",
|
"title": "Git Server",
|
||||||
"subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.",
|
"subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "You can connect and create a new user here:"
|
"1": "You can connect and create a new user here:"
|
||||||
|
@ -147,7 +158,7 @@
|
||||||
"initializing": {
|
"initializing": {
|
||||||
"_comment": "initializing page",
|
"_comment": "initializing page",
|
||||||
"1": "Connect Hetzner server",
|
"1": "Connect Hetzner server",
|
||||||
"2": "Here, your data and SelfPrivacy services wiil reside",
|
"2": "Here, your data and SelfPrivacy services wiil reside",
|
||||||
"how": "How to obtain API token",
|
"how": "How to obtain API token",
|
||||||
"3": "Connect CloudFlare",
|
"3": "Connect CloudFlare",
|
||||||
"4": "To manage your domain's DNS",
|
"4": "To manage your domain's DNS",
|
||||||
|
@ -157,6 +168,7 @@
|
||||||
"8": "Loading domains list",
|
"8": "Loading domains list",
|
||||||
"9": "Found more than one domain. For your own security, please be asked to delete unnecessary domains",
|
"9": "Found more than one domain. For your own security, please be asked to delete unnecessary domains",
|
||||||
"10": "Save domain",
|
"10": "Save domain",
|
||||||
|
"final": "Final step",
|
||||||
"11": "Create server",
|
"11": "Create server",
|
||||||
"what": "What does it mean?",
|
"what": "What does it mean?",
|
||||||
"13": "Server rebooted. Waiting for the last verification...",
|
"13": "Server rebooted. Waiting for the last verification...",
|
||||||
|
@ -166,6 +178,18 @@
|
||||||
"17": "Check",
|
"17": "Check",
|
||||||
"18": "How to obtain Hetzner API Token",
|
"18": "How to obtain Hetzner API Token",
|
||||||
"19": "1 Go via this link ",
|
"19": "1 Go via this link ",
|
||||||
"20": "\n"
|
"20": "\n",
|
||||||
|
"21": "One more restart to apply your security certificates."
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"_comment": "messages in modals",
|
||||||
|
"1": "Server with such name, already exist",
|
||||||
|
"2": "Destroy server and create a new one?",
|
||||||
|
"3": "Are you sure?",
|
||||||
|
"4": "Purge all authentication keys?",
|
||||||
|
"5": "Yes, purge all my tokens"
|
||||||
|
},
|
||||||
|
"timer": {
|
||||||
|
"sec": "{} sec"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,18 +20,28 @@
|
||||||
"domain": "Домен",
|
"domain": "Домен",
|
||||||
"saving": "Сохранение..",
|
"saving": "Сохранение..",
|
||||||
"nickname": "Никнейм",
|
"nickname": "Никнейм",
|
||||||
"loading": "Загрузка"
|
"loading": "Загрузка",
|
||||||
|
"later": "Настрою потом",
|
||||||
|
"reset": "Reset",
|
||||||
|
"details": "Детальная информация",
|
||||||
|
"no_data": "Нет данных"
|
||||||
},
|
},
|
||||||
"more": {
|
"more": {
|
||||||
"_comment": "вкладка еще",
|
"_comment": "вкладка еще",
|
||||||
"configuration_wizard": "Мастер Подключения",
|
"configuration_wizard": "Мастер Подключения",
|
||||||
"settings": "Настройки приложения",
|
|
||||||
"about_project": "О проекте SelfPrivacy",
|
"about_project": "О проекте SelfPrivacy",
|
||||||
"about_app": "О приложении",
|
"about_app": "О приложении",
|
||||||
"onboarding": "Onboarding",
|
"onboarding": "Приветствие",
|
||||||
"console": "Console",
|
"console": "Консоль",
|
||||||
"about_app_page": {
|
"about_app_page": {
|
||||||
"text": "Тут любая служебная информация, v.{}"
|
"text": "Версия приложения: v.{}"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Настройки приложения",
|
||||||
|
"1": "Темная тема",
|
||||||
|
"2": "Сменить цветовую тему",
|
||||||
|
"3": "Сброс настроек",
|
||||||
|
"4": "Сбросить API ключи а так же root пользвателя"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"onboarding": {
|
"onboarding": {
|
||||||
|
@ -46,26 +56,27 @@
|
||||||
"page_title": "Ваш Дата-центр",
|
"page_title": "Ваш Дата-центр",
|
||||||
"server": {
|
"server": {
|
||||||
"card_title": "Сервер",
|
"card_title": "Сервер",
|
||||||
|
"status": "Статус — в норме",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "Это виртульный компьютер на котором работают все ваши сервисы.",
|
"1": "Это виртульный компьютер на котором работают все ваши сервисы.",
|
||||||
"2": "1 CPU, RAM 4Gb, 40Gb — $5 в месяц",
|
"2": "Общая информация",
|
||||||
"3": "Статус — в норме"
|
"3": "Размещение"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
"card_title": "Домен",
|
"card_title": "Домен",
|
||||||
|
"status": "Статус — в норме",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.",
|
"1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.",
|
||||||
"2": "{} — продлен до {}",
|
"2": "{} — продлен до {}"
|
||||||
"3": "Статус — в норме"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"backup": {
|
"backup": {
|
||||||
"card_title": "Резервное копирование",
|
"card_title": "Резервное копирование",
|
||||||
|
"status": "Статус — в норме",
|
||||||
"bottom_sheet": {
|
"bottom_sheet": {
|
||||||
"1": "Выручит в любой ситуации: хакерская атака, удаление сервера и т.п.",
|
"1": "Выручит в любой ситуации: хакерская атака, удаление сервера и т.п.",
|
||||||
"2": "3Gb — бестплатно до 10Gb, последний вчера в {}",
|
"2": "3Gb — бестплатно до 10Gb, последний вчера в {}"
|
||||||
"3": "Статус — в норме"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -136,8 +147,8 @@
|
||||||
"not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя",
|
"not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя",
|
||||||
"nobody_here": "'Здесь пока никого'",
|
"nobody_here": "'Здесь пока никого'",
|
||||||
"login": "Логин",
|
"login": "Логин",
|
||||||
"onboarding": "Onboarding",
|
"onboarding": "Приветствие",
|
||||||
"console": "Console",
|
"console": "Консоль разработчика",
|
||||||
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.",
|
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.",
|
||||||
"delete_confirm_question": "удалить учетную запись?",
|
"delete_confirm_question": "удалить учетную запись?",
|
||||||
"reset_password": "Сбросить пароль",
|
"reset_password": "Сбросить пароль",
|
||||||
|
@ -157,6 +168,7 @@
|
||||||
"8": "Загружаем список доменов",
|
"8": "Загружаем список доменов",
|
||||||
"9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены",
|
"9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены",
|
||||||
"10": "Сохранить домен",
|
"10": "Сохранить домен",
|
||||||
|
"final": "Последний шаг",
|
||||||
"11": "Создать сервер",
|
"11": "Создать сервер",
|
||||||
"what": "Что это значит?",
|
"what": "Что это значит?",
|
||||||
"13": "Сервер презагружен, ждем последнюю проверку",
|
"13": "Сервер презагружен, ждем последнюю проверку",
|
||||||
|
@ -166,6 +178,18 @@
|
||||||
"17": "Проверка",
|
"17": "Проверка",
|
||||||
"18": "Как получить Hetzner API Token'",
|
"18": "Как получить Hetzner API Token'",
|
||||||
"19": "1 Переходим по ссылке ",
|
"19": "1 Переходим по ссылке ",
|
||||||
"20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет."
|
"20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.",
|
||||||
|
"21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности"
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"_comment": "messages in modals",
|
||||||
|
"1": "Сервер с таким именем уже существует",
|
||||||
|
"2": "Уничтожить сервер и создать новый?",
|
||||||
|
"3": "Вы уверенны",
|
||||||
|
"4": "Сбросить все ключи?",
|
||||||
|
"5": "Да, сбросить"
|
||||||
|
},
|
||||||
|
"timer": {
|
||||||
|
"sec": "{} сек"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
json_serializable:
|
||||||
|
options:
|
||||||
|
create_factory: true
|
||||||
|
create_to_json: false
|
|
@ -1,17 +1,23 @@
|
||||||
import 'package:get_it/get_it.dart';
|
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/console.dart';
|
||||||
import 'package:selfprivacy/logic/get_it/navigation.dart';
|
import 'package:selfprivacy/logic/get_it/navigation.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/console.dart';
|
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;
|
||||||
|
|
||||||
void getItSetup() {
|
Future<void> getItSetup() async {
|
||||||
getIt.registerSingleton<NavigationService>(NavigationService());
|
getIt.registerSingleton<NavigationService>(NavigationService());
|
||||||
|
|
||||||
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
||||||
getIt.registerSingleton<TimerModel>(TimerModel());
|
getIt.registerSingleton<TimerModel>(TimerModel());
|
||||||
|
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
|
||||||
|
|
||||||
|
await getIt.allReady();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class HiveConfig {
|
||||||
Hive.registerAdapter(HetznerServerDetailsAdapter());
|
Hive.registerAdapter(HetznerServerDetailsAdapter());
|
||||||
Hive.registerAdapter(CloudFlareDomainAdapter());
|
Hive.registerAdapter(CloudFlareDomainAdapter());
|
||||||
Hive.registerAdapter(BackblazeCredentialAdapter());
|
Hive.registerAdapter(BackblazeCredentialAdapter());
|
||||||
|
Hive.registerAdapter(HetznerDataBaseAdapter());
|
||||||
|
|
||||||
await Hive.openBox(BNames.appSettings);
|
await Hive.openBox(BNames.appSettings);
|
||||||
var cipher = HiveAesCipher(await getEncriptedKey());
|
var cipher = HiveAesCipher(await getEncriptedKey());
|
||||||
|
@ -55,5 +56,6 @@ class BNames {
|
||||||
static String isServerStarted = 'isServerStarted';
|
static String isServerStarted = 'isServerStarted';
|
||||||
static String backblazeKey = 'backblazeKey';
|
static String backblazeKey = 'backblazeKey';
|
||||||
static String isLoading = 'isLoading';
|
static String isLoading = 'isLoading';
|
||||||
static String isServerReseted = 'isServerReseted';
|
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
|
||||||
|
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,12 @@ final headline4Style = defaultTextStyle.copyWith(
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final headline5Style = defaultTextStyle.copyWith(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: NamedFontWeight.medium,
|
||||||
|
color: BrandColors.headlineColor.withOpacity(0.8),
|
||||||
|
);
|
||||||
|
|
||||||
final body1Style = defaultTextStyle;
|
final body1Style = defaultTextStyle;
|
||||||
final body2Style = defaultTextStyle.copyWith(
|
final body2Style = defaultTextStyle.copyWith(
|
||||||
color: BrandColors.textColor2,
|
color: BrandColors.textColor2,
|
||||||
|
@ -59,9 +65,9 @@ final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue);
|
||||||
final progressTextStyleLight = TextStyle(
|
final progressTextStyleLight = TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: BrandColors.textColor1,
|
color: BrandColors.textColor1,
|
||||||
|
height: 1.7,
|
||||||
);
|
);
|
||||||
|
|
||||||
final progressTextStyleDark = TextStyle(
|
final progressTextStyleDark = progressTextStyleLight.copyWith(
|
||||||
fontSize: 11,
|
|
||||||
color: BrandColors.white,
|
color: BrandColors.white,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,29 +1,41 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/adapter.dart';
|
import 'package:dio/adapter.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/get_it/console.dart';
|
import 'package:selfprivacy/logic/get_it/console.dart';
|
||||||
import 'package:selfprivacy/logic/models/message.dart';
|
import 'package:selfprivacy/logic/models/message.dart';
|
||||||
|
|
||||||
abstract class ApiMap {
|
abstract class ApiMap {
|
||||||
ApiMap() {
|
Future<Dio> getClient() async {
|
||||||
var client = Dio()..interceptors.add(ConsoleInterceptor());
|
var dio = Dio(await options);
|
||||||
(client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
if (hasLoger) {
|
||||||
|
dio.interceptors.add(PrettyDioLogger());
|
||||||
|
}
|
||||||
|
dio..interceptors.add(ConsoleInterceptor());
|
||||||
|
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
||||||
(HttpClient client) {
|
(HttpClient client) {
|
||||||
client.badCertificateCallback =
|
client.badCertificateCallback =
|
||||||
(X509Certificate cert, String host, int port) => true;
|
(X509Certificate cert, String host, int port) => true;
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
loggedClient = client;
|
return dio;
|
||||||
}
|
}
|
||||||
String? rootAddress;
|
|
||||||
|
|
||||||
late Dio loggedClient;
|
FutureOr<BaseOptions> get options;
|
||||||
|
|
||||||
void close() {
|
abstract final String rootAddress;
|
||||||
loggedClient.close();
|
abstract final bool hasLoger;
|
||||||
|
abstract final bool isWithToken;
|
||||||
|
|
||||||
|
ValidateStatus? validateStatus;
|
||||||
|
|
||||||
|
void close(Dio client) {
|
||||||
|
client.close();
|
||||||
|
validateStatus = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,36 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
||||||
|
|
||||||
class BackblazeApi extends ApiMap {
|
class BackblazeApi extends ApiMap {
|
||||||
BackblazeApi([String? token]) {
|
BackblazeApi({this.hasLoger = false, this.isWithToken = true});
|
||||||
if (token != null) {
|
|
||||||
loggedClient.options = BaseOptions(
|
BaseOptions get options {
|
||||||
headers: {'Authorization': 'Basic $token'},
|
var options = BaseOptions(baseUrl: rootAddress);
|
||||||
baseUrl: rootAddress!,
|
if (isWithToken) {
|
||||||
);
|
var backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
||||||
|
var token = backblazeCredential!.applicationKey;
|
||||||
|
options.headers = {'Authorization': 'Basic $token'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validateStatus != null) {
|
||||||
|
options.validateStatus = validateStatus!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? rootAddress =
|
String rootAddress = 'https://api.backblazeb2.com/b2api/v2/';
|
||||||
'https://api.backblazeb2.com/b2api/v2/b2_authorize_account';
|
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(String encodedApiKey) async {
|
||||||
var options = Options(
|
var client = await getClient();
|
||||||
headers: {'Authorization': 'Basic $token'},
|
Response response = await client.get(
|
||||||
validateStatus: (status) {
|
'b2_authorize_account',
|
||||||
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
close(client);
|
||||||
Response response = await loggedClient.get(rootAddress!, options: options);
|
|
||||||
|
|
||||||
if (response.statusCode == HttpStatus.ok) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return true;
|
return true;
|
||||||
} else if (response.statusCode == HttpStatus.unauthorized) {
|
} else if (response.statusCode == HttpStatus.unauthorized) {
|
||||||
|
@ -34,4 +39,10 @@ class BackblazeApi extends ApiMap {
|
||||||
throw Exception('code: ${response.statusCode}');
|
throw Exception('code: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@override
|
||||||
|
bool hasLoger;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isWithToken;
|
||||||
|
}
|
||||||
|
|
|
@ -1,30 +1,40 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
||||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/dns_records.dart';
|
import 'package:selfprivacy/logic/models/dns_records.dart';
|
||||||
|
|
||||||
class CloudflareApi extends ApiMap {
|
class CloudflareApi extends ApiMap {
|
||||||
CloudflareApi([String? token]) {
|
CloudflareApi({this.hasLoger = false, this.isWithToken = true});
|
||||||
if (token != null) {
|
|
||||||
loggedClient.options =
|
BaseOptions get options {
|
||||||
BaseOptions(headers: {'Authorization': 'Bearer $token'});
|
var options = BaseOptions(baseUrl: rootAddress);
|
||||||
|
if (isWithToken) {
|
||||||
|
var token = getIt<ApiConfigModel>().cloudFlareKey;
|
||||||
|
assert(token != null);
|
||||||
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validateStatus != null) {
|
||||||
|
options.validateStatus = validateStatus!;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? rootAddress = 'https://api.cloudflare.com/client/v4';
|
String rootAddress = 'https://api.cloudflare.com/client/v4';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(String token) async {
|
||||||
var url = '$rootAddress/user/tokens/verify';
|
validateStatus = (status) {
|
||||||
var options = Options(
|
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
};
|
||||||
validateStatus: (status) {
|
|
||||||
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Response response = await loggedClient.get(url, options: options);
|
var client = await getClient();
|
||||||
|
Response response = await client.get('/user/tokens/verify',
|
||||||
|
options: Options(headers: {'Authorization': 'Bearer $token'}));
|
||||||
|
|
||||||
|
close(client);
|
||||||
|
|
||||||
if (response.statusCode == HttpStatus.ok) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -35,27 +45,19 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getZoneId(String? token, String domain) async {
|
Future<String> getZoneId(String domain) async {
|
||||||
var url = '$rootAddress/zones';
|
validateStatus = (status) {
|
||||||
|
return status == HttpStatus.ok || status == HttpStatus.forbidden;
|
||||||
var options = Options(
|
};
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
var client = await getClient();
|
||||||
validateStatus: (status) {
|
Response response = await client.get(
|
||||||
return status == HttpStatus.ok || status == HttpStatus.forbidden;
|
'/zones',
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Response response = await loggedClient.get(
|
|
||||||
url,
|
|
||||||
options: options,
|
|
||||||
queryParameters: {'name': domain},
|
queryParameters: {'name': domain},
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
close(client);
|
||||||
return response.data['result'][0]['id'];
|
|
||||||
} catch (error) {
|
return response.data['result'][0]['id'];
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeSimilarRecords({
|
Future<void> removeSimilarRecords({
|
||||||
|
@ -65,20 +67,24 @@ class CloudflareApi extends ApiMap {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
var domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
var domainZoneId = cloudFlareDomain.zoneId;
|
||||||
|
|
||||||
var url = '$rootAddress/zones/$domainZoneId/dns_records';
|
var url = '/zones/$domainZoneId/dns_records';
|
||||||
|
|
||||||
|
var client = await getClient();
|
||||||
|
Response response = await client.get(url);
|
||||||
|
|
||||||
var response = await loggedClient.get(url);
|
|
||||||
List records = response.data['result'] ?? [];
|
List records = response.data['result'] ?? [];
|
||||||
var allDeleteFutures = <Future>[];
|
var allDeleteFutures = <Future>[];
|
||||||
|
|
||||||
for (var record in records) {
|
for (var record in records) {
|
||||||
if (record['zone_name'] == domainName) {
|
if (record['zone_name'] == domainName) {
|
||||||
allDeleteFutures.add(
|
allDeleteFutures.add(
|
||||||
loggedClient.delete('$url/${record["id"]}'),
|
client.delete('$url/${record["id"]}'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(allDeleteFutures);
|
await Future.wait(allDeleteFutures);
|
||||||
|
close(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createMultipleDnsRecords({
|
Future<void> createMultipleDnsRecords({
|
||||||
|
@ -92,10 +98,11 @@ class CloudflareApi extends ApiMap {
|
||||||
var url = '$rootAddress/zones/$domainZoneId/dns_records';
|
var url = '$rootAddress/zones/$domainZoneId/dns_records';
|
||||||
|
|
||||||
var allCreateFutures = <Future>[];
|
var allCreateFutures = <Future>[];
|
||||||
|
var client = await getClient();
|
||||||
|
|
||||||
for (var record in listDnsRecords) {
|
for (var record in listDnsRecords) {
|
||||||
allCreateFutures.add(
|
allCreateFutures.add(
|
||||||
loggedClient.post(
|
client.post(
|
||||||
url,
|
url,
|
||||||
data: record.toJson(),
|
data: record.toJson(),
|
||||||
),
|
),
|
||||||
|
@ -103,23 +110,9 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(allCreateFutures);
|
await Future.wait(allCreateFutures);
|
||||||
|
close(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
// setDkim(String dkimRecordString, String domainZoneId) {
|
|
||||||
// var txt3 = DnsRecords(
|
|
||||||
// type: 'TXT',
|
|
||||||
// name: 'selector._domainkey',
|
|
||||||
// content: dkimRecordString,
|
|
||||||
// ttl: 18000,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// var url = '$rootAddress/zones/$domainZoneId/dns_records';
|
|
||||||
// loggedClient.post(
|
|
||||||
// url,
|
|
||||||
// data: txt3.toJson(),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
List<DnsRecords> projectDnsRecords(String? domainName, String? ip4) {
|
List<DnsRecords> projectDnsRecords(String? domainName, String? ip4) {
|
||||||
var domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
|
var domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
|
||||||
|
|
||||||
|
@ -161,15 +154,24 @@ class CloudflareApi extends ApiMap {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>?> domainList() async {
|
Future<List<String>> domainList() async {
|
||||||
var url = '$rootAddress/zones?per_page=50';
|
var url = '$rootAddress/zones?per_page=50';
|
||||||
var response = await loggedClient.get(
|
var client = await getClient();
|
||||||
|
|
||||||
|
var response = await client.get(
|
||||||
url,
|
url,
|
||||||
queryParameters: {'per_page': 50},
|
queryParameters: {'per_page': 50},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
close(client);
|
||||||
return response.data['result']
|
return response.data['result']
|
||||||
.map<String>((el) => el['name'] as String?)
|
.map<String>((el) => el['name'] as String)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool hasLoger;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isWithToken;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,33 +2,49 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
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/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
import 'package:selfprivacy/utils/password_generator2.dart';
|
import 'package:selfprivacy/utils/password_generator2.dart';
|
||||||
|
|
||||||
class HetznerApi extends ApiMap {
|
class HetznerApi extends ApiMap {
|
||||||
HetznerApi([String? token]) {
|
bool hasLoger;
|
||||||
if (token != null) {
|
bool isWithToken;
|
||||||
loggedClient.options = BaseOptions(
|
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
HetznerApi({this.hasLoger = false, this.isWithToken = true});
|
||||||
baseUrl: rootAddress!,
|
|
||||||
);
|
BaseOptions get options {
|
||||||
|
var options = BaseOptions(baseUrl: rootAddress);
|
||||||
|
if (isWithToken) {
|
||||||
|
var token = getIt<ApiConfigModel>().hetznerKey;
|
||||||
|
assert(token != null);
|
||||||
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validateStatus != null) {
|
||||||
|
options.validateStatus = validateStatus!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? rootAddress = 'https://api.hetzner.cloud/v1/servers';
|
String rootAddress = 'https://api.hetzner.cloud/v1';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(String token) async {
|
||||||
var options = Options(
|
validateStatus = (status) {
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
||||||
validateStatus: (status) {
|
};
|
||||||
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
var client = await getClient();
|
||||||
},
|
Response response = await client.get(
|
||||||
|
'/servers',
|
||||||
|
options: Options(
|
||||||
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
close(client);
|
||||||
Response response = await loggedClient.get(rootAddress!, options: options);
|
|
||||||
|
|
||||||
if (response.statusCode == HttpStatus.ok) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -39,56 +55,128 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isFreeToCreate() async {
|
||||||
|
var client = await getClient();
|
||||||
|
|
||||||
|
Response serversReponse = await client.get('/servers');
|
||||||
|
List servers = serversReponse.data['servers'];
|
||||||
|
var server = servers.firstWhere(
|
||||||
|
(el) => el['name'] == 'selfprivacy-server',
|
||||||
|
orElse: null,
|
||||||
|
);
|
||||||
|
client.close();
|
||||||
|
return server == null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> createServer({
|
Future<HetznerServerDetails> createServer({
|
||||||
required String? cloudFlareKey,
|
required String cloudFlareKey,
|
||||||
required User rootUser,
|
required User rootUser,
|
||||||
required String? domainName,
|
required String domainName,
|
||||||
}) async {
|
}) async {
|
||||||
var dbPassword = getRandomString(40);
|
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,
|
||||||
|
"labels": {"labelkey": "value"},
|
||||||
|
"location": "fsn1",
|
||||||
|
"automount": false,
|
||||||
|
"format": "ext4"
|
||||||
|
},
|
||||||
|
);
|
||||||
|
var dbId = dbCreateResponse.data['volume']['id'];
|
||||||
var data = jsonDecode(
|
var data = jsonDecode(
|
||||||
'''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}''',
|
'''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''',
|
||||||
);
|
);
|
||||||
|
|
||||||
Response response = await loggedClient.post(
|
Response serverCreateResponse = await client.post(
|
||||||
rootAddress!,
|
'/servers',
|
||||||
data: data,
|
data: data,
|
||||||
);
|
);
|
||||||
|
client.close();
|
||||||
return HetznerServerDetails(
|
return HetznerServerDetails(
|
||||||
id: response.data['server']['id'],
|
id: serverCreateResponse.data['server']['id'],
|
||||||
ip4: response.data['server']['public_net']['ipv4']['ip'],
|
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
|
||||||
createTime: DateTime.now(),
|
createTime: DateTime.now(),
|
||||||
|
dataBase: HetznerDataBase(
|
||||||
|
id: dbId,
|
||||||
|
name: dbCreateResponse.data['volume']['name'],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteSelfprivacyServer({
|
Future<void> deleteSelfprivacyServerAndAllVolumes({
|
||||||
required String? cloudFlareKey,
|
required String domainName,
|
||||||
}) async {
|
}) async {
|
||||||
Response response = await loggedClient.get(rootAddress!);
|
var client = await getClient();
|
||||||
|
|
||||||
List list = response.data['servers'];
|
Response serversReponse = await client.get('/servers');
|
||||||
var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server');
|
List servers = serversReponse.data['servers'];
|
||||||
await loggedClient.delete('$rootAddress/${server['id']}');
|
var server = servers.firstWhere((el) => el['name'] == domainName);
|
||||||
|
await client.delete('/servers/${server['id']}');
|
||||||
|
|
||||||
|
Response volumesReponse = await client.get('/volumes');
|
||||||
|
List volumes = volumesReponse.data['volumes'];
|
||||||
|
|
||||||
|
var laterFutures = <Future>[];
|
||||||
|
for (var volume in volumes) {
|
||||||
|
if (volume['server'] == null) {
|
||||||
|
await client.delete('/volumes/${volume['id']}');
|
||||||
|
} else {
|
||||||
|
laterFutures.add(Future.delayed(Duration(seconds: 60)).then(
|
||||||
|
(_) => client.delete('/volumes/${volume['id']}'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (laterFutures.isEmpty) {
|
||||||
|
close(client);
|
||||||
|
} else {
|
||||||
|
Future.wait(laterFutures).then((value) => close(client));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> startServer({
|
Future<HetznerServerDetails> reset() async {
|
||||||
required HetznerServerDetails server,
|
var server = getIt<ApiConfigModel>().hetznerServer!;
|
||||||
}) async {
|
|
||||||
await loggedClient.post('/${server.id}/actions/poweron');
|
|
||||||
|
|
||||||
return server.copyWith(
|
var client = await getClient();
|
||||||
startTime: DateTime.now(),
|
await client.post('/servers/${server.id}/actions/reset');
|
||||||
);
|
close(client);
|
||||||
|
|
||||||
|
return server.copyWith(startTime: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> restart({
|
Future<HetznerServerDetails> powerOn() async {
|
||||||
required HetznerServerDetails server,
|
var server = getIt<ApiConfigModel>().hetznerServer!;
|
||||||
}) async {
|
|
||||||
await loggedClient.post('/${server.id}/actions/poweron');
|
|
||||||
|
|
||||||
return server.copyWith(
|
var client = await getClient();
|
||||||
startTime: DateTime.now(),
|
await client.post('/servers/${server.id}/actions/poweron');
|
||||||
);
|
close(client);
|
||||||
|
|
||||||
|
return server.copyWith(startTime: DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics() async {
|
||||||
|
var hetznerServer = getIt<ApiConfigModel>().hetznerServer;
|
||||||
|
var client = await getClient();
|
||||||
|
await client.post('/servers/${hetznerServer!.id}/metrics');
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<HetznerServerInfo> getInfo() async {
|
||||||
|
var hetznerServer = getIt<ApiConfigModel>().hetznerServer;
|
||||||
|
var client = await getClient();
|
||||||
|
Response response = await client.get('/servers/${hetznerServer!.id}');
|
||||||
|
close(client);
|
||||||
|
|
||||||
|
return HetznerServerInfo.fromJson(response.data!['server']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,45 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
|
||||||
import 'api_map.dart';
|
import 'api_map.dart';
|
||||||
|
|
||||||
class ServerApi extends ApiMap {
|
class ServerApi extends ApiMap {
|
||||||
ServerApi(String? domainName) {
|
bool hasLoger;
|
||||||
loggedClient.options = BaseOptions(
|
bool isWithToken;
|
||||||
baseUrl: 'https://api.$domainName',
|
|
||||||
);
|
ServerApi({this.hasLoger = false, this.isWithToken = true});
|
||||||
|
|
||||||
|
BaseOptions get options {
|
||||||
|
var options = BaseOptions();
|
||||||
|
|
||||||
|
if (isWithToken) {
|
||||||
|
var cloudFlareDomain = getIt<ApiConfigModel>().cloudFlareDomain;
|
||||||
|
var domainName = cloudFlareDomain!.domainName;
|
||||||
|
|
||||||
|
options = BaseOptions(baseUrl: 'https://api.$domainName');
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isHttpServerWorking() async {
|
Future<bool> isHttpServerWorking() async {
|
||||||
bool res;
|
bool res;
|
||||||
|
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
|
var client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await loggedClient.get('/serviceStatus');
|
response = await client.get('/serviceStatus');
|
||||||
res = response.statusCode == HttpStatus.ok;
|
res = response.statusCode == HttpStatus.ok;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res = false;
|
res = false;
|
||||||
}
|
}
|
||||||
|
close(client);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<String> getDkim(String domainName) async {
|
String get rootAddress =>
|
||||||
// var response = await loggedClient.get(
|
throw UnimplementedError('not used in with implementation');
|
||||||
// '/getDKIM',
|
|
||||||
// options: Options(responseType: ResponseType.plain),
|
|
||||||
// );
|
|
||||||
// return _decodeAndCutData(response.data, domainName);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String _decodeAndCutData(String text, String domainName) {
|
|
||||||
// var decodedTextString = text.substring(1, text.length - 1);
|
|
||||||
// var stringToBase64 = utf8.fuse(base64);
|
|
||||||
|
|
||||||
// return stringToBase64
|
|
||||||
// .decode(decodedTextString)
|
|
||||||
// .replaceAll("selector._domainkey IN TXT ( ", "")
|
|
||||||
// .replaceAll("\"\n \"", "")
|
|
||||||
// .replaceAll(' ) ; ----- DKIM key selector for $domainName\n', '');
|
|
||||||
// }
|
|
||||||
|
|
|
@ -35,7 +35,10 @@ part 'app_config_state.dart';
|
||||||
/// c. if server is ok wait 30 sec
|
/// c. if server is ok wait 30 sec
|
||||||
/// d. reset server
|
/// d. reset server
|
||||||
///
|
///
|
||||||
/// 2.3. a. wait 60sec |finishCheckIfServerIsOkay
|
/// 2.3. a. wait 60sec |oneMoreReset()
|
||||||
|
/// d. reset server
|
||||||
|
///
|
||||||
|
/// 2.4. a. wait 30sec |finishCheckIfServerIsOkay
|
||||||
/// b. checkServer
|
/// b. checkServer
|
||||||
/// c. if server is okay set that fully checked
|
/// c. if server is okay set that fully checked
|
||||||
|
|
||||||
|
@ -44,21 +47,18 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
|
|
||||||
final repository = AppConfigRepository();
|
final repository = AppConfigRepository();
|
||||||
|
|
||||||
void load() {
|
Future<void> load() async {
|
||||||
var state = repository.load();
|
var state = await repository.load();
|
||||||
|
|
||||||
if (state.progress < 6 || state.isFullyInitilized) {
|
if (state.progress < 6 || state.isFullyInitilized) {
|
||||||
emit(state);
|
emit(state);
|
||||||
} else if (state.progress == 6) {
|
} else if (state.progress == 6) {
|
||||||
print('startServerIfDnsIsOkay');
|
|
||||||
|
|
||||||
startServerIfDnsIsOkay(state: state, isImmediate: true);
|
startServerIfDnsIsOkay(state: state, isImmediate: true);
|
||||||
} else if (state.progress == 7) {
|
} else if (state.progress == 7) {
|
||||||
print('resetServerIfServerIsOkay');
|
|
||||||
|
|
||||||
resetServerIfServerIsOkay(state: state, isImmediate: true);
|
resetServerIfServerIsOkay(state: state, isImmediate: true);
|
||||||
} else if (state.progress == 8) {
|
} else if (state.progress == 8) {
|
||||||
print('finishCheckIfServerIsOkay');
|
oneMoreReset(state: state, isImmediate: true);
|
||||||
|
} else if (state.progress == 9) {
|
||||||
finishCheckIfServerIsOkay(state: state, isImmediate: true);
|
finishCheckIfServerIsOkay(state: state, isImmediate: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,11 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
|
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
var server = await repository.startServer(
|
var server = await repository.startServer(
|
||||||
state.hetznerKey,
|
|
||||||
state.hetznerServer!,
|
state.hetznerServer!,
|
||||||
);
|
);
|
||||||
repository.saveServerDetails(server);
|
await repository.saveServerDetails(server);
|
||||||
|
await repository.saveIsServerStarted(true);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isServerStarted: true,
|
isServerStarted: true,
|
||||||
|
@ -110,41 +111,92 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetServerIfServerIsOkay({
|
void oneMoreReset({
|
||||||
AppConfigState? state,
|
AppConfigState? state,
|
||||||
bool isImmediate = false,
|
bool isImmediate = false,
|
||||||
}) async {
|
}) async {
|
||||||
state = state ?? this.state;
|
var dataState = state ?? this.state;
|
||||||
|
|
||||||
var work = () async {
|
var work = () async {
|
||||||
emit(TimerState(dataState: state!, isLoading: true));
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking(
|
var isServerWorking = await repository.isHttpServerWorking();
|
||||||
state.cloudFlareDomain!.domainName,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
var pauseDuration = Duration(seconds: 30);
|
var pauseDuration = Duration(seconds: 30);
|
||||||
emit(TimerState(
|
emit(TimerState(
|
||||||
dataState: state,
|
dataState: dataState,
|
||||||
timerStart: DateTime.now(),
|
timerStart: DateTime.now(),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
duration: pauseDuration,
|
duration: pauseDuration,
|
||||||
));
|
));
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
var hetznerServerDetails = await repository.restart(
|
var hetznerServerDetails = await repository.restart();
|
||||||
state!.hetznerKey,
|
await repository.saveIsServerResetedSecondTime(true);
|
||||||
state.hetznerServer!,
|
await repository.saveServerDetails(hetznerServerDetails);
|
||||||
);
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
dataState.copyWith(
|
||||||
isServerReseted: true,
|
isServerResetedSecondTime: true,
|
||||||
hetznerServer: hetznerServerDetails,
|
hetznerServer: hetznerServerDetails,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
finishCheckIfServerIsOkay();
|
finishCheckIfServerIsOkay();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
oneMoreReset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (isImmediate) {
|
||||||
|
work();
|
||||||
|
} else {
|
||||||
|
var pauseDuration = Duration(seconds: 60);
|
||||||
|
emit(
|
||||||
|
TimerState(
|
||||||
|
dataState: dataState,
|
||||||
|
timerStart: DateTime.now(),
|
||||||
|
duration: pauseDuration,
|
||||||
|
isLoading: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
timer = Timer(pauseDuration, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetServerIfServerIsOkay({
|
||||||
|
AppConfigState? state,
|
||||||
|
bool isImmediate = false,
|
||||||
|
}) async {
|
||||||
|
var dataState = state ?? this.state;
|
||||||
|
|
||||||
|
var work = () async {
|
||||||
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
|
var isServerWorking = await repository.isHttpServerWorking();
|
||||||
|
|
||||||
|
if (isServerWorking) {
|
||||||
|
var pauseDuration = Duration(seconds: 30);
|
||||||
|
emit(TimerState(
|
||||||
|
dataState: dataState,
|
||||||
|
timerStart: DateTime.now(),
|
||||||
|
isLoading: false,
|
||||||
|
duration: pauseDuration,
|
||||||
|
));
|
||||||
|
timer = Timer(pauseDuration, () async {
|
||||||
|
var hetznerServerDetails = await repository.restart();
|
||||||
|
await repository.saveIsServerResetedFirstTime(true);
|
||||||
|
await repository.saveServerDetails(hetznerServerDetails);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
dataState.copyWith(
|
||||||
|
isServerResetedFirstTime: true,
|
||||||
|
hetznerServer: hetznerServerDetails,
|
||||||
|
isLoading: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
oneMoreReset();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
resetServerIfServerIsOkay();
|
resetServerIfServerIsOkay();
|
||||||
}
|
}
|
||||||
|
@ -155,7 +207,7 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
var pauseDuration = Duration(seconds: 60);
|
var pauseDuration = Duration(seconds: 60);
|
||||||
emit(
|
emit(
|
||||||
TimerState(
|
TimerState(
|
||||||
dataState: state,
|
dataState: dataState,
|
||||||
timerStart: DateTime.now(),
|
timerStart: DateTime.now(),
|
||||||
duration: pauseDuration,
|
duration: pauseDuration,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
@ -176,12 +228,15 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
var work = () async {
|
var work = () async {
|
||||||
emit(TimerState(dataState: state!, isLoading: true));
|
emit(TimerState(dataState: state!, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking(
|
var isServerWorking = await repository.isHttpServerWorking();
|
||||||
state.cloudFlareDomain!.domainName,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
emit(state.copyWith(hasFinalChecked: true, isLoading: false));
|
await repository.saveHasFinalChecked(true);
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
hasFinalChecked: true,
|
||||||
|
isLoading: false,
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
finishCheckIfServerIsOkay();
|
finishCheckIfServerIsOkay();
|
||||||
}
|
}
|
||||||
|
@ -203,37 +258,37 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearAppConfig() {
|
void clearAppConfig() {
|
||||||
_closeTimer();
|
closeTimer();
|
||||||
repository.clearAppConfig();
|
repository.clearAppConfig();
|
||||||
emit(InitialAppConfigState());
|
emit(InitialAppConfigState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHetznerKey(String hetznerKey) {
|
void setHetznerKey(String hetznerKey) async {
|
||||||
repository.saveHetznerKey(hetznerKey);
|
await repository.saveHetznerKey(hetznerKey);
|
||||||
emit(state.copyWith(hetznerKey: hetznerKey));
|
emit(state.copyWith(hetznerKey: hetznerKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCloudflareKey(String cloudFlareKey) {
|
void setCloudflareKey(String cloudFlareKey) async {
|
||||||
repository.saveCloudFlare(cloudFlareKey);
|
await repository.saveCloudFlareKey(cloudFlareKey);
|
||||||
emit(state.copyWith(cloudFlareKey: cloudFlareKey));
|
emit(state.copyWith(cloudFlareKey: cloudFlareKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBackblazeKey(String keyId, String applicationKey) {
|
void setBackblazeKey(String keyId, String applicationKey) async {
|
||||||
var backblazeCredential = BackblazeCredential(
|
var backblazeCredential = BackblazeCredential(
|
||||||
keyId: keyId,
|
keyId: keyId,
|
||||||
applicationKey: applicationKey,
|
applicationKey: applicationKey,
|
||||||
);
|
);
|
||||||
repository.saveBackblazeKey(backblazeCredential);
|
await repository.saveBackblazeKey(backblazeCredential);
|
||||||
emit(state.copyWith(backblazeCredential: backblazeCredential));
|
emit(state.copyWith(backblazeCredential: backblazeCredential));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDomain(CloudFlareDomain cloudFlareDomain) {
|
void setDomain(CloudFlareDomain cloudFlareDomain) async {
|
||||||
repository.saveDomain(cloudFlareDomain);
|
await repository.saveDomain(cloudFlareDomain);
|
||||||
emit(state.copyWith(cloudFlareDomain: cloudFlareDomain));
|
emit(state.copyWith(cloudFlareDomain: cloudFlareDomain));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRootUser(User rootUser) {
|
void setRootUser(User rootUser) async {
|
||||||
repository.saveRootUser(rootUser);
|
await repository.saveRootUser(rootUser);
|
||||||
emit(state.copyWith(rootUser: rootUser));
|
emit(state.copyWith(rootUser: rootUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +296,6 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
AppConfigState _stateCopy = state;
|
AppConfigState _stateCopy = state;
|
||||||
var onSuccess = (serverDetails) async {
|
var onSuccess = (serverDetails) async {
|
||||||
await repository.createDnsRecords(
|
await repository.createDnsRecords(
|
||||||
state.cloudFlareKey,
|
|
||||||
serverDetails.ip4,
|
serverDetails.ip4,
|
||||||
state.cloudFlareDomain!,
|
state.cloudFlareDomain!,
|
||||||
);
|
);
|
||||||
|
@ -258,25 +312,23 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
try {
|
try {
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
await repository.createServer(
|
await repository.createServer(
|
||||||
state.hetznerKey,
|
|
||||||
state.rootUser!,
|
state.rootUser!,
|
||||||
state.cloudFlareDomain!.domainName,
|
state.cloudFlareDomain!.domainName,
|
||||||
state.cloudFlareKey,
|
state.cloudFlareKey!,
|
||||||
onCancel: onCancel,
|
onCancel: onCancel,
|
||||||
onSuccess: onSuccess,
|
onSuccess: onSuccess,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addError(e);
|
|
||||||
emit(_stateCopy);
|
emit(_stateCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
_closeTimer();
|
closeTimer();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closeTimer() {
|
void closeTimer() {
|
||||||
if (timer != null && timer!.isActive) {
|
if (timer != null && timer!.isActive) {
|
||||||
timer!.cancel();
|
timer!.cancel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
|
import 'package:selfprivacy/logic/get_it/api_config.dart';
|
||||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_details.dart';
|
import 'package:selfprivacy/logic/models/server_details.dart';
|
||||||
|
@ -15,68 +16,46 @@ import 'package:basic_utils/basic_utils.dart';
|
||||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
||||||
import 'app_config_cubit.dart';
|
import 'app_config_cubit.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class AppConfigRepository {
|
class AppConfigRepository {
|
||||||
Box box = Hive.box(BNames.appConfig);
|
Box box = Hive.box(BNames.appConfig);
|
||||||
|
|
||||||
AppConfigState load() {
|
Future<AppConfigState> load() async {
|
||||||
return AppConfigState(
|
var res = AppConfigState(
|
||||||
hetznerKey: box.get(BNames.hetznerKey),
|
hetznerKey: getIt<ApiConfigModel>().hetznerKey,
|
||||||
cloudFlareKey: box.get(BNames.cloudFlareKey),
|
cloudFlareKey: getIt<ApiConfigModel>().cloudFlareKey,
|
||||||
cloudFlareDomain: box.get(BNames.cloudFlareDomain),
|
cloudFlareDomain: getIt<ApiConfigModel>().cloudFlareDomain,
|
||||||
backblazeCredential: box.get(BNames.backblazeKey),
|
backblazeCredential: getIt<ApiConfigModel>().backblazeCredential,
|
||||||
|
hetznerServer: getIt<ApiConfigModel>().hetznerServer,
|
||||||
rootUser: box.get(BNames.rootUser),
|
rootUser: box.get(BNames.rootUser),
|
||||||
hetznerServer: box.get(BNames.hetznerServer),
|
|
||||||
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
|
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
|
||||||
error: null,
|
isServerResetedFirstTime:
|
||||||
|
box.get(BNames.isServerResetedFirstTime, defaultValue: false),
|
||||||
|
isServerResetedSecondTime:
|
||||||
|
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
||||||
hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false),
|
hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false),
|
||||||
|
error: null,
|
||||||
isLoading: box.get(BNames.isLoading, defaultValue: false),
|
isLoading: box.get(BNames.isLoading, defaultValue: false),
|
||||||
isServerReseted: box.get(BNames.isServerReseted, defaultValue: false),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearAppConfig() {
|
void clearAppConfig() {
|
||||||
box.clear();
|
box.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveHetznerKey(String key) {
|
|
||||||
box.put(BNames.hetznerKey, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveBackblazeKey(BackblazeCredential backblazeCredential) {
|
|
||||||
box.put(BNames.backblazeKey, backblazeCredential);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveCloudFlare(String key) {
|
|
||||||
box.put(BNames.cloudFlareKey, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveDomain(CloudFlareDomain cloudFlareDomain) {
|
|
||||||
box.put(BNames.cloudFlareDomain, cloudFlareDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveRootUser(User rootUser) {
|
|
||||||
box.put(BNames.rootUser, rootUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<HetznerServerDetails> startServer(
|
Future<HetznerServerDetails> startServer(
|
||||||
String? hetznerKey,
|
|
||||||
HetznerServerDetails hetznerServer,
|
HetznerServerDetails hetznerServer,
|
||||||
) async {
|
) async {
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
var hetznerApi = HetznerApi();
|
||||||
var serverDetails = await hetznerApi.startServer(server: hetznerServer);
|
var serverDetails = await hetznerApi.powerOn();
|
||||||
hetznerApi.close();
|
|
||||||
box.put(BNames.isServerStarted, true);
|
|
||||||
|
|
||||||
return serverDetails;
|
return serverDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveServerDetails(HetznerServerDetails serverDetails) async {
|
|
||||||
await box.put(BNames.hetznerServer, serverDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> isDnsAddressesMatch(String? domainName, String? ip4) async {
|
Future<bool> isDnsAddressesMatch(String? domainName, String? ip4) async {
|
||||||
print(domainName);
|
|
||||||
var addresses = <String>[
|
var addresses = <String>[
|
||||||
'$domainName',
|
'$domainName',
|
||||||
'api.$domainName',
|
'api.$domainName',
|
||||||
|
@ -110,20 +89,18 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box.put(BNames.hasFinalChecked, true);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createServer(
|
Future<void> createServer(
|
||||||
String? hetznerKey,
|
|
||||||
User rootUser,
|
User rootUser,
|
||||||
String? domainName,
|
String domainName,
|
||||||
String? cloudFlareKey, {
|
String cloudFlareKey, {
|
||||||
void Function()? onCancel,
|
required void Function() onCancel,
|
||||||
required Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
|
required Future<void> Function(HetznerServerDetails serverDetails)
|
||||||
|
onSuccess,
|
||||||
}) async {
|
}) async {
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
var hetznerApi = HetznerApi();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var serverDetails = await hetznerApi.createServer(
|
var serverDetails = await hetznerApi.createServer(
|
||||||
|
@ -131,41 +108,37 @@ class AppConfigRepository {
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
);
|
);
|
||||||
await box.put(BNames.hetznerServer, serverDetails);
|
saveServerDetails(serverDetails);
|
||||||
hetznerApi.close();
|
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
||||||
var nav = getIt.get<NavigationService>();
|
var nav = getIt.get<NavigationService>();
|
||||||
nav.showPopUpDialog(
|
nav.showPopUpDialog(
|
||||||
BrandAlert(
|
BrandAlert(
|
||||||
title: 'Сервер с таким именем уже существует',
|
title: 'modals.1'.tr(),
|
||||||
contentText: 'Уничтожить сервер и создать новый?',
|
contentText: 'modals.2'.tr(),
|
||||||
acitons: [
|
acitons: [
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'Удалить',
|
text: 'basis.delete'.tr(),
|
||||||
isRed: true,
|
isRed: true,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await hetznerApi.deleteSelfprivacyServer(
|
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
||||||
cloudFlareKey: cloudFlareKey,
|
domainName: domainName);
|
||||||
);
|
|
||||||
|
|
||||||
var serverDetails = await hetznerApi.createServer(
|
var serverDetails = await hetznerApi.createServer(
|
||||||
cloudFlareKey: cloudFlareKey,
|
cloudFlareKey: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
);
|
);
|
||||||
hetznerApi.close();
|
|
||||||
|
|
||||||
await box.put(BNames.hetznerServer, serverDetails);
|
await saveServerDetails(serverDetails);
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'Отменить',
|
text: 'basis.cancel'.tr(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
hetznerApi.close();
|
onCancel();
|
||||||
onCancel!();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -176,11 +149,10 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createDnsRecords(
|
Future<void> createDnsRecords(
|
||||||
String? cloudFlareKey,
|
|
||||||
String? ip4,
|
String? ip4,
|
||||||
CloudFlareDomain cloudFlareDomain,
|
CloudFlareDomain cloudFlareDomain,
|
||||||
) async {
|
) async {
|
||||||
var cloudflareApi = CloudflareApi(cloudFlareKey);
|
var cloudflareApi = CloudflareApi();
|
||||||
|
|
||||||
await cloudflareApi.removeSimilarRecords(
|
await cloudflareApi.removeSimilarRecords(
|
||||||
ip4: ip4,
|
ip4: ip4,
|
||||||
|
@ -191,22 +163,61 @@ class AppConfigRepository {
|
||||||
ip4: ip4,
|
ip4: ip4,
|
||||||
cloudFlareDomain: cloudFlareDomain,
|
cloudFlareDomain: cloudFlareDomain,
|
||||||
);
|
);
|
||||||
|
|
||||||
cloudflareApi.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isHttpServerWorking(String? domainName) async {
|
Future<bool> isHttpServerWorking() async {
|
||||||
var api = ServerApi(domainName);
|
var api = ServerApi();
|
||||||
var isHttpServerWorking = await api.isHttpServerWorking();
|
var isHttpServerWorking = await api.isHttpServerWorking();
|
||||||
api.close();
|
|
||||||
return isHttpServerWorking;
|
return isHttpServerWorking;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> restart(
|
Future<HetznerServerDetails> restart() async {
|
||||||
String? hetznerKey,
|
var hetznerApi = HetznerApi();
|
||||||
HetznerServerDetails server,
|
return await hetznerApi.reset();
|
||||||
) async {
|
}
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
|
||||||
return await hetznerApi.restart(server: server);
|
Future<HetznerServerDetails> powerOn() async {
|
||||||
|
var hetznerApi = HetznerApi();
|
||||||
|
return await hetznerApi.powerOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveServerDetails(HetznerServerDetails serverDetails) async {
|
||||||
|
await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveHetznerKey(String key) async {
|
||||||
|
await getIt<ApiConfigModel>().storeHetznerKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveBackblazeKey(BackblazeCredential backblazeCredential) async {
|
||||||
|
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveCloudFlareKey(String key) async {
|
||||||
|
await getIt<ApiConfigModel>().storeCloudFlareKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveDomain(CloudFlareDomain cloudFlareDomain) async {
|
||||||
|
await getIt<ApiConfigModel>().storeCloudFlareDomain(cloudFlareDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveIsServerStarted(bool value) async {
|
||||||
|
await box.put(BNames.isServerStarted, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveIsServerResetedFirstTime(bool value) async {
|
||||||
|
await box.put(BNames.isServerResetedFirstTime, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveIsServerResetedSecondTime(bool value) async {
|
||||||
|
await box.put(BNames.isServerResetedSecondTime, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveRootUser(User rootUser) async {
|
||||||
|
await box.put(BNames.rootUser, rootUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveHasFinalChecked(bool value) async {
|
||||||
|
await box.put(BNames.hasFinalChecked, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ class AppConfigState extends Equatable {
|
||||||
required this.rootUser,
|
required this.rootUser,
|
||||||
required this.hetznerServer,
|
required this.hetznerServer,
|
||||||
required this.isServerStarted,
|
required this.isServerStarted,
|
||||||
required this.isServerReseted,
|
required this.isServerResetedFirstTime,
|
||||||
|
required this.isServerResetedSecondTime,
|
||||||
required this.hasFinalChecked,
|
required this.hasFinalChecked,
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.error,
|
required this.error,
|
||||||
|
@ -24,7 +25,7 @@ class AppConfigState extends Equatable {
|
||||||
rootUser,
|
rootUser,
|
||||||
hetznerServer,
|
hetznerServer,
|
||||||
isServerStarted,
|
isServerStarted,
|
||||||
isServerReseted,
|
isServerResetedFirstTime,
|
||||||
hasFinalChecked,
|
hasFinalChecked,
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
|
@ -36,9 +37,11 @@ class AppConfigState extends Equatable {
|
||||||
final CloudFlareDomain? cloudFlareDomain;
|
final CloudFlareDomain? cloudFlareDomain;
|
||||||
final User? rootUser;
|
final User? rootUser;
|
||||||
final HetznerServerDetails? hetznerServer;
|
final HetznerServerDetails? hetznerServer;
|
||||||
final bool? isServerStarted;
|
final bool isServerStarted;
|
||||||
final bool? isServerReseted;
|
final bool isServerResetedFirstTime;
|
||||||
final bool? hasFinalChecked;
|
final bool isServerResetedSecondTime;
|
||||||
|
|
||||||
|
final bool hasFinalChecked;
|
||||||
|
|
||||||
final bool? isLoading;
|
final bool? isLoading;
|
||||||
final Exception? error;
|
final Exception? error;
|
||||||
|
@ -51,7 +54,8 @@ class AppConfigState extends Equatable {
|
||||||
User? rootUser,
|
User? rootUser,
|
||||||
HetznerServerDetails? hetznerServer,
|
HetznerServerDetails? hetznerServer,
|
||||||
bool? isServerStarted,
|
bool? isServerStarted,
|
||||||
bool? isServerReseted,
|
bool? isServerResetedFirstTime,
|
||||||
|
bool? isServerResetedSecondTime,
|
||||||
bool? hasFinalChecked,
|
bool? hasFinalChecked,
|
||||||
bool? isLoading,
|
bool? isLoading,
|
||||||
Exception? error,
|
Exception? error,
|
||||||
|
@ -64,7 +68,10 @@ class AppConfigState extends Equatable {
|
||||||
rootUser: rootUser ?? this.rootUser,
|
rootUser: rootUser ?? this.rootUser,
|
||||||
hetznerServer: hetznerServer ?? this.hetznerServer,
|
hetznerServer: hetznerServer ?? this.hetznerServer,
|
||||||
isServerStarted: isServerStarted ?? this.isServerStarted,
|
isServerStarted: isServerStarted ?? this.isServerStarted,
|
||||||
isServerReseted: isServerReseted ?? this.isServerReseted,
|
isServerResetedFirstTime:
|
||||||
|
isServerResetedFirstTime ?? this.isServerResetedFirstTime,
|
||||||
|
isServerResetedSecondTime:
|
||||||
|
isServerResetedSecondTime ?? this.isServerResetedSecondTime,
|
||||||
hasFinalChecked: hasFinalChecked ?? this.hasFinalChecked,
|
hasFinalChecked: hasFinalChecked ?? this.hasFinalChecked,
|
||||||
isLoading: isLoading ?? this.isLoading,
|
isLoading: isLoading ?? this.isLoading,
|
||||||
error: error ?? this.error,
|
error: error ?? this.error,
|
||||||
|
@ -80,17 +87,22 @@ class AppConfigState extends Equatable {
|
||||||
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
|
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
|
||||||
int get progress => _fulfilementList.where((el) => el!).length;
|
int get progress => _fulfilementList.where((el) => el!).length;
|
||||||
|
|
||||||
List<bool?> get _fulfilementList => [
|
List<bool?> get _fulfilementList {
|
||||||
isHetznerFilled,
|
var res = [
|
||||||
isCloudFlareFilled,
|
isHetznerFilled,
|
||||||
isBackblazeFilled,
|
isCloudFlareFilled,
|
||||||
isDomainFilled,
|
isBackblazeFilled,
|
||||||
isUserFilled,
|
isDomainFilled,
|
||||||
isServerCreated,
|
isUserFilled,
|
||||||
isServerStarted,
|
isServerCreated,
|
||||||
isServerReseted,
|
isServerStarted,
|
||||||
hasFinalChecked,
|
isServerResetedFirstTime,
|
||||||
];
|
isServerResetedSecondTime,
|
||||||
|
hasFinalChecked,
|
||||||
|
];
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InitialAppConfigState extends AppConfigState {
|
class InitialAppConfigState extends AppConfigState {
|
||||||
|
@ -103,7 +115,8 @@ class InitialAppConfigState extends AppConfigState {
|
||||||
rootUser: null,
|
rootUser: null,
|
||||||
hetznerServer: null,
|
hetznerServer: null,
|
||||||
isServerStarted: false,
|
isServerStarted: false,
|
||||||
isServerReseted: false,
|
isServerResetedFirstTime: false,
|
||||||
|
isServerResetedSecondTime: false,
|
||||||
hasFinalChecked: false,
|
hasFinalChecked: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -124,7 +137,8 @@ class TimerState extends AppConfigState {
|
||||||
rootUser: dataState.rootUser,
|
rootUser: dataState.rootUser,
|
||||||
hetznerServer: dataState.hetznerServer,
|
hetznerServer: dataState.hetznerServer,
|
||||||
isServerStarted: dataState.isServerStarted,
|
isServerStarted: dataState.isServerStarted,
|
||||||
isServerReseted: dataState.isServerReseted,
|
isServerResetedFirstTime: dataState.isServerResetedFirstTime,
|
||||||
|
isServerResetedSecondTime: dataState.isServerResetedSecondTime,
|
||||||
hasFinalChecked: dataState.hasFinalChecked,
|
hasFinalChecked: dataState.hasFinalChecked,
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
error: dataState.error,
|
error: dataState.error,
|
||||||
|
|
|
@ -5,8 +5,6 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||||
|
|
||||||
class BackblazeFormCubit extends FormCubit {
|
class BackblazeFormCubit extends FormCubit {
|
||||||
BackblazeApi apiClient = BackblazeApi();
|
|
||||||
|
|
||||||
BackblazeFormCubit(this.initializingCubit) {
|
BackblazeFormCubit(this.initializingCubit) {
|
||||||
//var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
//var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
||||||
keyId = FieldCubit(
|
keyId = FieldCubit(
|
||||||
|
@ -29,7 +27,7 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
super.setFields([keyId, applicationKey]);
|
super.addFields([keyId, applicationKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -42,14 +40,14 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<String> keyId;
|
late final FieldCubit<String> keyId;
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<String> applicationKey;
|
late final FieldCubit<String> applicationKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
|
BackblazeApi apiClient = BackblazeApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String encodedApiKey = encodedBackblazeKey(
|
String encodedApiKey = encodedBackblazeKey(
|
||||||
keyId.state.value,
|
keyId.state.value,
|
||||||
|
@ -67,11 +65,4 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> close() async {
|
|
||||||
apiClient.close();
|
|
||||||
|
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
|
|
||||||
class CloudFlareFormCubit extends FormCubit {
|
class CloudFlareFormCubit extends FormCubit {
|
||||||
CloudflareApi apiClient = CloudflareApi();
|
|
||||||
|
|
||||||
CloudFlareFormCubit(this.initializingCubit) {
|
CloudFlareFormCubit(this.initializingCubit) {
|
||||||
var regExp = RegExp(r"\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
var regExp = RegExp(r"\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
|
@ -20,7 +18,7 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
super.setFields([apiKey]);
|
super.addFields([apiKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,6 +33,7 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
|
CloudflareApi apiClient = CloudflareApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
|
@ -51,8 +50,6 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
apiClient.close();
|
|
||||||
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,15 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
|
|
||||||
class DomainSetupCubit extends Cubit<DomainSetupState> {
|
class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
DomainSetupCubit(this.initializingCubit) : super(Initial()) {
|
DomainSetupCubit(this.initializingCubit) : super(Initial());
|
||||||
var token = initializingCubit.state.cloudFlareKey;
|
|
||||||
|
|
||||||
assert(token != null, 'no cloudflare token');
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
api = CloudflareApi(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppConfigCubit initializingCubit;
|
|
||||||
late CloudflareApi api;
|
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
emit(Loading(LoadingTypes.loadingDomain));
|
emit(Loading(LoadingTypes.loadingDomain));
|
||||||
var list = await (api.domainList() as Future<List<String>>);
|
var api = CloudflareApi();
|
||||||
|
|
||||||
|
var list = await api.domainList();
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
emit(Empty());
|
emit(Empty());
|
||||||
} else if (list.length == 1) {
|
} else if (list.length == 1) {
|
||||||
|
@ -29,20 +24,17 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
api.close();
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveDomain() async {
|
Future<void> saveDomain() async {
|
||||||
assert(state is Loaded, 'wrong state');
|
assert(state is Loaded, 'wrong state');
|
||||||
var domainName = (state as Loaded).domain;
|
var domainName = (state as Loaded).domain;
|
||||||
|
var api = CloudflareApi();
|
||||||
|
|
||||||
emit(Loading(LoadingTypes.saving));
|
emit(Loading(LoadingTypes.saving));
|
||||||
|
|
||||||
var zoneId = await api.getZoneId(
|
var zoneId = await api.getZoneId(domainName);
|
||||||
initializingCubit.state.cloudFlareKey,
|
|
||||||
domainName,
|
|
||||||
);
|
|
||||||
|
|
||||||
var domain = CloudFlareDomain(
|
var domain = CloudFlareDomain(
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
// import 'dart:async';
|
|
||||||
|
|
||||||
// import 'package:cubit_form/cubit_form.dart';
|
|
||||||
// import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
|
||||||
// import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
|
||||||
// import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
|
||||||
|
|
||||||
// class DomainFormCubit extends FormCubit {
|
|
||||||
// CloudflareApi apiClient = CloudflareApi();
|
|
||||||
|
|
||||||
// DomainFormCubit(this.initializingCubit) {
|
|
||||||
// var regExp =
|
|
||||||
// RegExp(r"^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}");
|
|
||||||
// domainName = FieldCubit(
|
|
||||||
// initalValue: '',
|
|
||||||
// validations: [
|
|
||||||
// RequiredStringValidation('required'),
|
|
||||||
// ValidationModel<String>(
|
|
||||||
// (s) => !regExp.hasMatch(s),
|
|
||||||
// 'invalid domain format',
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
|
|
||||||
// super.setFields([domainName]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// FutureOr<void> onSubmit() async {
|
|
||||||
// var domain = CloudFlareDomain(
|
|
||||||
// domainName: domainName.state.value,
|
|
||||||
// zoneId: zoneId,
|
|
||||||
// );
|
|
||||||
// initializingCubit.setDomain(domain);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final AppConfigCubit initializingCubit;
|
|
||||||
|
|
||||||
// FieldCubit<String> domainName;
|
|
||||||
// String zoneId;
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// FutureOr<bool> asyncValidation() async {
|
|
||||||
// var key = initializingCubit.state.cloudFlareKey;
|
|
||||||
|
|
||||||
// String zoneId;
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// zoneId = await apiClient.getZoneId(key, domainName.state.value);
|
|
||||||
// } catch (e) {
|
|
||||||
// addError(e);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (zoneId == null) {
|
|
||||||
// domainName.setError('Domain not in the list');
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// this.zoneId = zoneId;
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Future<void> close() async {
|
|
||||||
// apiClient.close();
|
|
||||||
|
|
||||||
// return super.close();
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -6,8 +6,6 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
|
|
||||||
class HetznerFormCubit extends FormCubit {
|
class HetznerFormCubit extends FormCubit {
|
||||||
HetznerApi apiClient = HetznerApi();
|
|
||||||
|
|
||||||
HetznerFormCubit(this.initializingCubit) {
|
HetznerFormCubit(this.initializingCubit) {
|
||||||
var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
|
@ -20,7 +18,7 @@ class HetznerFormCubit extends FormCubit {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
super.setFields([apiKey]);
|
super.addFields([apiKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -30,12 +28,13 @@ class HetznerFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<String> apiKey;
|
late final FieldCubit<String> apiKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
|
HetznerApi apiClient = HetznerApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -48,11 +47,4 @@ class HetznerFormCubit extends FormCubit {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> close() async {
|
|
||||||
apiClient.close();
|
|
||||||
|
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
|
|
||||||
class RootUserFormCubit extends FormCubit {
|
class RootUserFormCubit extends FormCubit {
|
||||||
HetznerApi apiClient = HetznerApi();
|
|
||||||
|
|
||||||
RootUserFormCubit(this.initializingCubit) {
|
RootUserFormCubit(this.initializingCubit) {
|
||||||
var userRegExp = RegExp(r"\W");
|
var userRegExp = RegExp(r"\W");
|
||||||
var passwordRegExp = RegExp(r"[\n\r\s]+");
|
var passwordRegExp = RegExp(r"[\n\r\s]+");
|
||||||
|
@ -32,7 +29,7 @@ class RootUserFormCubit extends FormCubit {
|
||||||
|
|
||||||
isVisible = FieldCubit(initalValue: false);
|
isVisible = FieldCubit(initalValue: false);
|
||||||
|
|
||||||
super.setFields([userName, password, isVisible]);
|
super.addFields([userName, password, isVisible]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -46,17 +43,7 @@ class RootUserFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<String> userName;
|
late final FieldCubit<String> userName;
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<String> password;
|
late final FieldCubit<String> password;
|
||||||
// ignore: close_sinks
|
|
||||||
late final FieldCubit<bool> isVisible;
|
late final FieldCubit<bool> isVisible;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> close() async {
|
|
||||||
apiClient.close();
|
|
||||||
|
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class UserFormCubit extends FormCubit {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
super.setFields([login, password]);
|
super.addFields([login, password]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -45,7 +45,6 @@ class UserFormCubit extends FormCubit {
|
||||||
usersCubit.addUser(user);
|
usersCubit.addUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: close_sinks
|
|
||||||
late FieldCubit<String> login;
|
late FieldCubit<String> login;
|
||||||
late FieldCubit<String> password;
|
late FieldCubit<String> password;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hetzner_server_info.dart';
|
||||||
|
|
||||||
|
part 'server_detailed_info_state.dart';
|
||||||
|
|
||||||
|
class ServerDetailsCubit extends Cubit<ServerDetailsState> {
|
||||||
|
ServerDetailsCubit() : super(ServerDetailsInitial());
|
||||||
|
|
||||||
|
ServerDetailsRepository repository = ServerDetailsRepository();
|
||||||
|
|
||||||
|
void check() async {
|
||||||
|
var isReadyToCheck = getIt<ApiConfigModel>().hetznerServer != null;
|
||||||
|
if (isReadyToCheck) {
|
||||||
|
emit(ServerDetailsLoading());
|
||||||
|
var data = await repository.load();
|
||||||
|
emit(Loaded(serverInfo: data, checkTime: DateTime.now()));
|
||||||
|
} else {
|
||||||
|
emit(ServerDetailsNotReady());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hetzner_server_info.dart';
|
||||||
|
|
||||||
|
class ServerDetailsRepository {
|
||||||
|
Future<HetznerServerInfo> load() async {
|
||||||
|
var client = HetznerApi();
|
||||||
|
return await client.getInfo();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
part of 'server_detailed_info_cubit.dart';
|
||||||
|
|
||||||
|
abstract class ServerDetailsState extends Equatable {
|
||||||
|
const ServerDetailsState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerDetailsInitial extends ServerDetailsState {}
|
||||||
|
|
||||||
|
class ServerDetailsLoading extends ServerDetailsState {}
|
||||||
|
|
||||||
|
class ServerDetailsNotReady extends ServerDetailsState {}
|
||||||
|
|
||||||
|
class Loading extends ServerDetailsState {}
|
||||||
|
|
||||||
|
class Loaded extends ServerDetailsState {
|
||||||
|
final HetznerServerInfo serverInfo;
|
||||||
|
final DateTime checkTime;
|
||||||
|
|
||||||
|
Loaded({
|
||||||
|
required this.serverInfo,
|
||||||
|
required this.checkTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [serverInfo, checkTime];
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
// import 'package:bloc/bloc.dart';
|
|
||||||
// import 'package:equatable/equatable.dart';
|
|
||||||
// import 'package:meta/meta.dart';
|
|
||||||
// import 'package:selfprivacy/logic/models/service.dart';
|
|
||||||
// import 'package:selfprivacy/logic/models/state_types.dart';
|
|
||||||
|
|
||||||
// export 'package:provider/provider.dart';
|
|
||||||
// export 'package:selfprivacy/logic/models/state_types.dart';
|
|
||||||
|
|
||||||
// part 'services_state.dart';
|
|
||||||
|
|
||||||
// class ServicesCubit extends Cubit<ServicesState> {
|
|
||||||
// ServicesCubit() : super(ServicesState(all));
|
|
||||||
|
|
||||||
// void connect(Service service) {
|
|
||||||
// var newState = state.updateElement(service, StateType.stable);
|
|
||||||
// emit(newState);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final all = ServiceTypes.values
|
|
||||||
// .map(
|
|
||||||
// (type) => Service(
|
|
||||||
// state: StateType.uninitialized,
|
|
||||||
// type: type,
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// .toList();
|
|
|
@ -1,26 +0,0 @@
|
||||||
// part of 'services_cubit.dart';
|
|
||||||
|
|
||||||
// @immutable
|
|
||||||
// class ServicesState extends Equatable{
|
|
||||||
// ServicesState(this.all);
|
|
||||||
|
|
||||||
// final List<Service> all;
|
|
||||||
|
|
||||||
// ServicesState updateElement(Service service, StateType newState) {
|
|
||||||
// var newList = [...all];
|
|
||||||
// var index = newList.indexOf(service);
|
|
||||||
// newList[index] = service.updateState(newState);
|
|
||||||
// return ServicesState(newList);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// List<Service> get connected => all
|
|
||||||
// .where((service) => service.state != StateType.uninitialized)
|
|
||||||
// .toList();
|
|
||||||
|
|
||||||
// List<Service> get uninitialized => all
|
|
||||||
// .where((service) => service.state == StateType.uninitialized)
|
|
||||||
// .toList();
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// List<Object> get props => all;
|
|
||||||
// }
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_details.dart';
|
||||||
|
|
||||||
|
class ApiConfigModel {
|
||||||
|
Box _box = Hive.box(BNames.appConfig);
|
||||||
|
|
||||||
|
HetznerServerDetails? get hetznerServer => _hetznerServer;
|
||||||
|
String? get hetznerKey => _hetznerKey;
|
||||||
|
String? get cloudFlareKey => _cloudFlareKey;
|
||||||
|
BackblazeCredential? get backblazeCredential => _backblazeCredential;
|
||||||
|
CloudFlareDomain? get cloudFlareDomain => _cloudFlareDomain;
|
||||||
|
|
||||||
|
String? _hetznerKey;
|
||||||
|
String? _cloudFlareKey;
|
||||||
|
HetznerServerDetails? _hetznerServer;
|
||||||
|
BackblazeCredential? _backblazeCredential;
|
||||||
|
CloudFlareDomain? _cloudFlareDomain;
|
||||||
|
|
||||||
|
Future<void> storeHetznerKey(String value) async {
|
||||||
|
await _box.put(BNames.hetznerKey, value);
|
||||||
|
_hetznerKey = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeCloudFlareKey(String value) async {
|
||||||
|
await _box.put(BNames.cloudFlareKey, value);
|
||||||
|
_cloudFlareKey = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeBackblazeCredential(BackblazeCredential value) async {
|
||||||
|
await _box.put(BNames.backblazeKey, value);
|
||||||
|
|
||||||
|
_backblazeCredential = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeCloudFlareDomain(CloudFlareDomain value) async {
|
||||||
|
await _box.put(BNames.cloudFlareDomain, value);
|
||||||
|
_cloudFlareDomain = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeServerDetails(HetznerServerDetails value) async {
|
||||||
|
await _box.put(BNames.hetznerServer, value);
|
||||||
|
_hetznerServer = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
_hetznerKey = null;
|
||||||
|
_cloudFlareKey = null;
|
||||||
|
_backblazeCredential = null;
|
||||||
|
_cloudFlareDomain = null;
|
||||||
|
_hetznerServer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
_hetznerKey = _box.get(BNames.hetznerKey);
|
||||||
|
_cloudFlareKey = _box.get(BNames.cloudFlareKey);
|
||||||
|
_backblazeCredential = _box.get(BNames.backblazeKey);
|
||||||
|
_cloudFlareDomain = _box.get(BNames.cloudFlareDomain);
|
||||||
|
_hetznerServer = _box.get(BNames.hetznerServer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
class TimerModel extends ChangeNotifier {
|
class TimerModel extends ChangeNotifier {
|
||||||
DateTime _time = DateTime.now();
|
DateTime _time = DateTime.now();
|
||||||
|
|
||||||
DateTime get messages => _time;
|
DateTime get time => _time;
|
||||||
|
|
||||||
void restart() {
|
void restart() {
|
||||||
_time = DateTime.now();
|
_time = DateTime.now();
|
||||||
|
|
|
@ -6,13 +6,13 @@ part 'backblaze_credential.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 4)
|
@HiveType(typeId: 4)
|
||||||
class BackblazeCredential {
|
class BackblazeCredential {
|
||||||
BackblazeCredential({this.keyId, this.applicationKey});
|
BackblazeCredential({required this.keyId, required this.applicationKey});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String? keyId;
|
final String keyId;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String? applicationKey;
|
final String applicationKey;
|
||||||
|
|
||||||
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ class BackblazeCredentialAdapter extends TypeAdapter<BackblazeCredential> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return BackblazeCredential(
|
return BackblazeCredential(
|
||||||
keyId: fields[0] as String?,
|
keyId: fields[0] as String,
|
||||||
applicationKey: fields[1] as String?,
|
applicationKey: fields[1] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,16 @@ part 'cloudflare_domain.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 3)
|
@HiveType(typeId: 3)
|
||||||
class CloudFlareDomain {
|
class CloudFlareDomain {
|
||||||
CloudFlareDomain({this.domainName, this.zoneId});
|
CloudFlareDomain({
|
||||||
|
required this.domainName,
|
||||||
|
required this.zoneId,
|
||||||
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String? domainName;
|
final String domainName;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String? zoneId;
|
final String zoneId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
|
@ -17,8 +17,8 @@ class CloudFlareDomainAdapter extends TypeAdapter<CloudFlareDomain> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return CloudFlareDomain(
|
return CloudFlareDomain(
|
||||||
domainName: fields[0] as String?,
|
domainName: fields[0] as String,
|
||||||
zoneId: fields[1] as String?,
|
zoneId: fields[1] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'hetzner_server_info.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class HetznerServerInfo {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final ServerStatus status;
|
||||||
|
final DateTime created;
|
||||||
|
|
||||||
|
@JsonKey(name: 'server_type')
|
||||||
|
final HetznerServerTypeInfo serverType;
|
||||||
|
|
||||||
|
@JsonKey(name: 'datacenter', fromJson: HetznerServerInfo.locationFromJson)
|
||||||
|
final HetznerLocation location;
|
||||||
|
|
||||||
|
static HetznerLocation locationFromJson(Map json) =>
|
||||||
|
HetznerLocation.fromJson(json['location']);
|
||||||
|
|
||||||
|
static HetznerServerInfo fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$HetznerServerInfoFromJson(json);
|
||||||
|
|
||||||
|
HetznerServerInfo(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.status,
|
||||||
|
this.created,
|
||||||
|
this.serverType,
|
||||||
|
this.location,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ServerStatus {
|
||||||
|
running,
|
||||||
|
initializing,
|
||||||
|
starting,
|
||||||
|
stopping,
|
||||||
|
off,
|
||||||
|
deleting,
|
||||||
|
migrating,
|
||||||
|
rebuilding,
|
||||||
|
unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class HetznerServerTypeInfo {
|
||||||
|
final int cores;
|
||||||
|
final num memory;
|
||||||
|
final int disk;
|
||||||
|
|
||||||
|
final List<HetznerPriceInfo> prices;
|
||||||
|
|
||||||
|
HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices);
|
||||||
|
|
||||||
|
static HetznerServerTypeInfo fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$HetznerServerTypeInfoFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class HetznerPriceInfo {
|
||||||
|
HetznerPriceInfo(this.hourly, this.monthly);
|
||||||
|
|
||||||
|
@JsonKey(name: 'price_hourly', fromJson: HetznerPriceInfo.getPrice)
|
||||||
|
final double hourly;
|
||||||
|
|
||||||
|
@JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice)
|
||||||
|
final double monthly;
|
||||||
|
|
||||||
|
static HetznerPriceInfo fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$HetznerPriceInfoFromJson(json);
|
||||||
|
|
||||||
|
static double getPrice(Map json) => double.parse(json['gross'] as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class HetznerLocation {
|
||||||
|
final String country;
|
||||||
|
final String city;
|
||||||
|
final String description;
|
||||||
|
|
||||||
|
@JsonKey(name: 'network_zone')
|
||||||
|
final String zone;
|
||||||
|
|
||||||
|
HetznerLocation(this.country, this.city, this.description, this.zone);
|
||||||
|
|
||||||
|
static HetznerLocation fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$HetznerLocationFromJson(json);
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'hetzner_server_info.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
HetznerServerInfo _$HetznerServerInfoFromJson(Map<String, dynamic> json) {
|
||||||
|
return HetznerServerInfo(
|
||||||
|
json['id'] as int,
|
||||||
|
json['name'] as String,
|
||||||
|
_$enumDecode(_$ServerStatusEnumMap, json['status']),
|
||||||
|
DateTime.parse(json['created'] as String),
|
||||||
|
HetznerServerTypeInfo.fromJson(json['server_type'] as Map<String, dynamic>),
|
||||||
|
HetznerServerInfo.locationFromJson(json['datacenter'] as Map),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
K _$enumDecode<K, V>(
|
||||||
|
Map<K, V> enumValues,
|
||||||
|
Object? source, {
|
||||||
|
K? unknownValue,
|
||||||
|
}) {
|
||||||
|
if (source == null) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'A value must be provided. Supported values: '
|
||||||
|
'${enumValues.values.join(', ')}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return enumValues.entries.singleWhere(
|
||||||
|
(e) => e.value == source,
|
||||||
|
orElse: () {
|
||||||
|
if (unknownValue == null) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'`$source` is not one of the supported values: '
|
||||||
|
'${enumValues.values.join(', ')}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return MapEntry(unknownValue, enumValues.values.first);
|
||||||
|
},
|
||||||
|
).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _$ServerStatusEnumMap = {
|
||||||
|
ServerStatus.running: 'running',
|
||||||
|
ServerStatus.initializing: 'initializing',
|
||||||
|
ServerStatus.starting: 'starting',
|
||||||
|
ServerStatus.stopping: 'stopping',
|
||||||
|
ServerStatus.off: 'off',
|
||||||
|
ServerStatus.deleting: 'deleting',
|
||||||
|
ServerStatus.migrating: 'migrating',
|
||||||
|
ServerStatus.rebuilding: 'rebuilding',
|
||||||
|
ServerStatus.unknown: 'unknown',
|
||||||
|
};
|
||||||
|
|
||||||
|
HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson(
|
||||||
|
Map<String, dynamic> json) {
|
||||||
|
return HetznerServerTypeInfo(
|
||||||
|
json['cores'] as int,
|
||||||
|
json['memory'] as num,
|
||||||
|
json['disk'] as int,
|
||||||
|
(json['prices'] as List<dynamic>)
|
||||||
|
.map((e) => HetznerPriceInfo.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) {
|
||||||
|
return HetznerPriceInfo(
|
||||||
|
HetznerPriceInfo.getPrice(json['price_hourly'] as Map),
|
||||||
|
HetznerPriceInfo.getPrice(json['price_monthly'] as Map),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) {
|
||||||
|
return HetznerLocation(
|
||||||
|
json['country'] as String,
|
||||||
|
json['city'] as String,
|
||||||
|
json['description'] as String,
|
||||||
|
json['network_zone'] as String,
|
||||||
|
);
|
||||||
|
}
|
|
@ -8,14 +8,15 @@ class HetznerServerDetails {
|
||||||
required this.ip4,
|
required this.ip4,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.createTime,
|
required this.createTime,
|
||||||
|
required this.dataBase,
|
||||||
this.startTime,
|
this.startTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String? ip4;
|
final String ip4;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final int? id;
|
final int id;
|
||||||
|
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
final DateTime? createTime;
|
final DateTime? createTime;
|
||||||
|
@ -23,14 +24,31 @@ class HetznerServerDetails {
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
final DateTime? startTime;
|
final DateTime? startTime;
|
||||||
|
|
||||||
|
@HiveField(4)
|
||||||
|
final HetznerDataBase dataBase;
|
||||||
|
|
||||||
HetznerServerDetails copyWith({DateTime? startTime}) {
|
HetznerServerDetails copyWith({DateTime? startTime}) {
|
||||||
return HetznerServerDetails(
|
return HetznerServerDetails(
|
||||||
startTime: startTime ?? this.startTime,
|
startTime: startTime ?? this.startTime,
|
||||||
createTime: createTime,
|
createTime: createTime,
|
||||||
id: id,
|
id: id,
|
||||||
ip4: ip4,
|
ip4: ip4,
|
||||||
|
dataBase: dataBase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String toString() => id.toString();
|
String toString() => id.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HiveType(typeId: 5)
|
||||||
|
class HetznerDataBase {
|
||||||
|
HetznerDataBase({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
@HiveField(1)
|
||||||
|
int id;
|
||||||
|
@HiveField(2)
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return HetznerServerDetails(
|
return HetznerServerDetails(
|
||||||
ip4: fields[0] as String?,
|
ip4: fields[0] as String,
|
||||||
id: fields[1] as int?,
|
id: fields[1] as int,
|
||||||
createTime: fields[3] as DateTime?,
|
createTime: fields[3] as DateTime?,
|
||||||
|
dataBase: fields[4] as HetznerDataBase,
|
||||||
startTime: fields[2] as DateTime?,
|
startTime: fields[2] as DateTime?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +28,7 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, HetznerServerDetails obj) {
|
void write(BinaryWriter writer, HetznerServerDetails obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(4)
|
..writeByte(5)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.ip4)
|
..write(obj.ip4)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
|
@ -35,7 +36,9 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
|
||||||
..writeByte(3)
|
..writeByte(3)
|
||||||
..write(obj.createTime)
|
..write(obj.createTime)
|
||||||
..writeByte(2)
|
..writeByte(2)
|
||||||
..write(obj.startTime);
|
..write(obj.startTime)
|
||||||
|
..writeByte(4)
|
||||||
|
..write(obj.dataBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -48,3 +51,40 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HetznerDataBaseAdapter extends TypeAdapter<HetznerDataBase> {
|
||||||
|
@override
|
||||||
|
final int typeId = 5;
|
||||||
|
|
||||||
|
@override
|
||||||
|
HetznerDataBase read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return HetznerDataBase(
|
||||||
|
id: fields[1] as int,
|
||||||
|
name: fields[2] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, HetznerDataBase obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(2)
|
||||||
|
..writeByte(1)
|
||||||
|
..write(obj.id)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is HetznerDataBaseAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
// import 'package:equatable/equatable.dart';
|
|
||||||
// import 'package:selfprivacy/logic/models/state_types.dart';
|
|
||||||
|
|
||||||
// enum ServiceTypes {
|
|
||||||
// messanger,
|
|
||||||
// mail,
|
|
||||||
// passwordManager,
|
|
||||||
// github,
|
|
||||||
// cloud,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class Service extends Equatable {
|
|
||||||
// const Service({required this.state, required this.type});
|
|
||||||
|
|
||||||
// final StateType state;
|
|
||||||
// final ServiceTypes type;
|
|
||||||
|
|
||||||
// Service updateState(StateType newState) => Service(
|
|
||||||
// state: newState,
|
|
||||||
// type: type,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// List<Object?> get props => [state, type];
|
|
||||||
// }
|
|
|
@ -15,12 +15,11 @@ import 'config/get_it_config.dart';
|
||||||
import 'config/localization.dart';
|
import 'config/localization.dart';
|
||||||
import 'logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'logic/cubit/app_settings/app_settings_cubit.dart';
|
||||||
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
await HiveConfig.init();
|
await HiveConfig.init();
|
||||||
Bloc.observer = SimpleBlocObserver();
|
Bloc.observer = SimpleBlocObserver();
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
getItSetup();
|
await getItSetup();
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class BrandButton {
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
|
|
||||||
static iconText({
|
static emptyWithIconText({
|
||||||
Key? key,
|
Key? key,
|
||||||
required VoidCallback onPressed,
|
required VoidCallback onPressed,
|
||||||
required String title,
|
required String title,
|
||||||
|
@ -100,6 +100,7 @@ class _TextButton extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onPressed,
|
onTap: onPressed,
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 48,
|
height: 48,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
|
@ -13,7 +13,6 @@ class BrandCard extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(bottom: 30),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? BrandColors.black
|
? BrandColors.black
|
||||||
|
|
|
@ -6,6 +6,7 @@ enum TextType {
|
||||||
h2, // cards titles
|
h2, // cards titles
|
||||||
h3, // titles in about page
|
h3, // titles in about page
|
||||||
h4, // caption
|
h4, // caption
|
||||||
|
h5, // Table data
|
||||||
body1, // normal
|
body1, // normal
|
||||||
body2, // with opacity
|
body2, // with opacity
|
||||||
medium,
|
medium,
|
||||||
|
@ -63,10 +64,28 @@ class BrandText extends StatelessWidget {
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
);
|
);
|
||||||
factory BrandText.h4(String? text, {TextStyle? style}) => BrandText(
|
factory BrandText.h4(
|
||||||
|
String? text, {
|
||||||
|
TextStyle? style,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
}) =>
|
||||||
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.h4,
|
type: TextType.h4,
|
||||||
style: style,
|
style: style,
|
||||||
|
textAlign: textAlign,
|
||||||
|
);
|
||||||
|
|
||||||
|
factory BrandText.h5(
|
||||||
|
String? text, {
|
||||||
|
TextStyle? style,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.h5,
|
||||||
|
style: style,
|
||||||
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
factory BrandText.body1(String? text, {TextStyle? style}) => BrandText(
|
factory BrandText.body1(String? text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
|
@ -123,6 +142,11 @@ class BrandText extends StatelessWidget {
|
||||||
? headline4Style.copyWith(color: Colors.white)
|
? headline4Style.copyWith(color: Colors.white)
|
||||||
: headline4Style;
|
: headline4Style;
|
||||||
break;
|
break;
|
||||||
|
case TextType.h5:
|
||||||
|
style = isDark
|
||||||
|
? headline5Style.copyWith(color: Colors.white)
|
||||||
|
: headline5Style;
|
||||||
|
break;
|
||||||
case TextType.body1:
|
case TextType.body1:
|
||||||
style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style;
|
style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class BrandTimer extends StatefulWidget {
|
class BrandTimer extends StatefulWidget {
|
||||||
const BrandTimer({
|
const BrandTimer({
|
||||||
|
@ -11,8 +12,8 @@ class BrandTimer extends StatefulWidget {
|
||||||
required this.duration,
|
required this.duration,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final DateTime? startDateTime;
|
final DateTime startDateTime;
|
||||||
final Duration? duration;
|
final Duration duration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_BrandTimerState createState() => _BrandTimerState();
|
_BrandTimerState createState() => _BrandTimerState();
|
||||||
|
@ -31,8 +32,8 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
_timerStart() {
|
_timerStart() {
|
||||||
_timeString = diffenceFromStart;
|
_timeString = diffenceFromStart;
|
||||||
timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
|
timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
|
||||||
var timePassed = DateTime.now().difference(widget.startDateTime!);
|
var timePassed = DateTime.now().difference(widget.startDateTime);
|
||||||
if (timePassed > widget.duration!) {
|
if (timePassed > widget.duration) {
|
||||||
t.cancel();
|
t.cancel();
|
||||||
} else {
|
} else {
|
||||||
_getTime();
|
_getTime();
|
||||||
|
@ -66,14 +67,14 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
String get diffenceFromStart =>
|
String get diffenceFromStart =>
|
||||||
_durationToString(DateTime.now().difference(widget.startDateTime!));
|
_durationToString(DateTime.now().difference(widget.startDateTime));
|
||||||
|
|
||||||
String _durationToString(Duration duration) {
|
String _durationToString(Duration duration) {
|
||||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||||
String twoDigitSeconds =
|
String twoDigitSeconds =
|
||||||
twoDigits(widget.duration!.inSeconds - duration.inSeconds.remainder(60));
|
twoDigits(widget.duration.inSeconds - duration.inSeconds.remainder(60));
|
||||||
|
|
||||||
return "$twoDigitSeconds cек";
|
return "timer.sec".tr(args: [twoDigitSeconds]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/pre_styled_buttons.dart';
|
||||||
|
|
||||||
|
class OnePage extends StatelessWidget {
|
||||||
|
const OnePage({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: PreferredSize(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 51,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: BrandText.h4('basis.details'.tr()),
|
||||||
|
),
|
||||||
|
BrandDivider(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
preferredSize: Size.fromHeight(52),
|
||||||
|
),
|
||||||
|
body: child,
|
||||||
|
bottomNavigationBar: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(boxShadow: kElevationToShadow[3]),
|
||||||
|
height: kBottomNavigationBarHeight,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: PreStyledButtons.close(
|
||||||
|
onPress: () => Navigator.of(context).pop()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
|
class PreStyledButtons {
|
||||||
|
static Widget close({
|
||||||
|
required VoidCallback onPress,
|
||||||
|
}) =>
|
||||||
|
_CloseButton(onPress: onPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CloseButton extends StatelessWidget {
|
||||||
|
const _CloseButton({Key? key, required this.onPress}) : super(key: key);
|
||||||
|
|
||||||
|
final VoidCallback onPress;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return OutlinedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
BrandText.h4('basis.close'.tr()),
|
||||||
|
Icon(Icons.close),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,25 +47,27 @@ class _ProgressBarState extends State<ProgressBar> {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
odd
|
odd.insert(
|
||||||
..insert(
|
0,
|
||||||
0,
|
SizedBox(
|
||||||
SizedBox(
|
width: 10,
|
||||||
width: 20,
|
),
|
||||||
),
|
);
|
||||||
)
|
even.add(
|
||||||
..add(
|
SizedBox(
|
||||||
SizedBox(
|
width: 10,
|
||||||
width: 10,
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
BrandText.h2('Progress'),
|
BrandText.h2('Progress'),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
Row(children: even),
|
Row(
|
||||||
|
children: even,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
),
|
||||||
SizedBox(height: 7),
|
SizedBox(height: 7),
|
||||||
Container(
|
Container(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
@ -96,12 +98,13 @@ class _ProgressBarState extends State<ProgressBar> {
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
Row(
|
Row(
|
||||||
children: odd,
|
children: odd,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expanded _stepTitle({
|
Container _stepTitle({
|
||||||
required int index,
|
required int index,
|
||||||
TextStyle? style,
|
TextStyle? style,
|
||||||
String? step,
|
String? step,
|
||||||
|
@ -110,17 +113,19 @@ class _ProgressBarState extends State<ProgressBar> {
|
||||||
var checked = index < widget.activeIndex;
|
var checked = index < widget.activeIndex;
|
||||||
|
|
||||||
style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style;
|
style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style;
|
||||||
return Expanded(
|
return Container(
|
||||||
flex: 2,
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
height: 20,
|
||||||
|
alignment: Alignment.center,
|
||||||
child: RichText(
|
child: RichText(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.justify,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: progressTextStyleLight,
|
style: progressTextStyleLight,
|
||||||
children: [
|
children: [
|
||||||
checked
|
checked
|
||||||
? WidgetSpan(
|
? WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 0, right: 2),
|
padding: const EdgeInsets.only(bottom: 2, right: 2),
|
||||||
child: Icon(BrandIcons.check, size: 11),
|
child: Icon(BrandIcons.check, size: 11),
|
||||||
))
|
))
|
||||||
: TextSpan(text: '${index + 1}.', style: style),
|
: TextSpan(text: '${index + 1}.', style: style),
|
||||||
|
|
|
@ -34,6 +34,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
() => _stepCheck(cubit),
|
() => _stepCheck(cubit),
|
||||||
() => _stepCheck(cubit),
|
() => _stepCheck(cubit),
|
||||||
() => _stepCheck(cubit),
|
() => _stepCheck(cubit),
|
||||||
|
() => _stepCheck(cubit),
|
||||||
() => Container(child: Text('Everythigng is initialized'))
|
() => Container(child: Text('Everythigng is initialized'))
|
||||||
][cubit.state.progress]();
|
][cubit.state.progress]();
|
||||||
return BlocListener<AppConfigCubit, AppConfigState>(
|
return BlocListener<AppConfigCubit, AppConfigState>(
|
||||||
|
@ -47,25 +48,21 @@ class InitializingPage extends StatelessWidget {
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: brandPagePadding1,
|
padding: brandPagePadding2.copyWith(top: 10, bottom: 10),
|
||||||
child: Column(
|
child: ProgressBar(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
steps: [
|
||||||
children: [
|
'Hetzner',
|
||||||
ProgressBar(
|
'CloudFlare',
|
||||||
steps: [
|
'Backblaze',
|
||||||
'Hetzner',
|
'Domain',
|
||||||
'CloudFlare',
|
'User',
|
||||||
'Backblaze',
|
'Server',
|
||||||
'Domain',
|
' ✅',
|
||||||
'User',
|
' ✅',
|
||||||
'Server',
|
' ✅',
|
||||||
' ✅',
|
' ✅',
|
||||||
' ✅',
|
|
||||||
' ✅'
|
|
||||||
],
|
|
||||||
activeIndex: cubit.state.progress,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
|
activeIndex: cubit.state.progress,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_addCard(
|
_addCard(
|
||||||
|
@ -77,14 +74,13 @@ class InitializingPage extends StatelessWidget {
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
title: cubit.state.isFullyInitilized
|
title: cubit.state.isFullyInitilized
|
||||||
? 'basis.close'.tr()
|
? 'basis.close'.tr()
|
||||||
: 'Настрою потом',
|
: 'basis.later'.tr(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pushAndRemoveUntil(
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
materialRoute(RootPage()),
|
materialRoute(RootPage()),
|
||||||
(predicate) => false,
|
(predicate) => false,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
SizedBox(height: 30),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -408,14 +404,15 @@ class InitializingPage extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Spacer(flex: 2),
|
Spacer(flex: 2),
|
||||||
BrandText.h2('initializing.how'.tr()),
|
BrandText.h2('initializing.final'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body2('initializing.11'.tr()),
|
BrandText.body2('initializing.11'.tr()),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed: isLoading!
|
||||||
isLoading! ? null : appConfigCubit.createServerAndSetDnsRecords,
|
? null
|
||||||
title: isLoading ? 'loading' : 'initializing.11'.tr(),
|
: () => appConfigCubit.createServerAndSetDnsRecords(),
|
||||||
|
title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(),
|
||||||
),
|
),
|
||||||
Spacer(flex: 2),
|
Spacer(flex: 2),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
|
@ -431,10 +428,12 @@ class InitializingPage extends StatelessWidget {
|
||||||
assert(appConfigCubit.state is TimerState, 'wronge state');
|
assert(appConfigCubit.state is TimerState, 'wronge state');
|
||||||
var state = appConfigCubit.state as TimerState;
|
var state = appConfigCubit.state as TimerState;
|
||||||
|
|
||||||
String? text;
|
late String? text;
|
||||||
if (state.isServerReseted!) {
|
if (state.isServerResetedSecondTime) {
|
||||||
text = 'initializing.13'.tr();
|
text = 'initializing.13'.tr();
|
||||||
} else if (state.isServerStarted!) {
|
} else if (state.isServerResetedFirstTime) {
|
||||||
|
text = 'initializing.21'.tr();
|
||||||
|
} else if (state.isServerStarted) {
|
||||||
text = 'initializing.14'.tr();
|
text = 'initializing.14'.tr();
|
||||||
} else if (state.isServerCreated) {
|
} else if (state.isServerCreated) {
|
||||||
text = 'initializing.15'.tr();
|
text = 'initializing.15'.tr();
|
||||||
|
@ -452,8 +451,8 @@ class InitializingPage extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
BrandText.body2('initializing.16'.tr()),
|
BrandText.body2('initializing.16'.tr()),
|
||||||
BrandTimer(
|
BrandTimer(
|
||||||
startDateTime: state.timerStart,
|
startDateTime: state.timerStart!,
|
||||||
duration: state.duration,
|
duration: state.duration!,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -472,7 +471,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
|
|
||||||
Widget _addCard(Widget child) {
|
Widget _addCard(Widget child) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 500,
|
height: 450,
|
||||||
padding: brandPagePadding2,
|
padding: brandPagePadding2,
|
||||||
child: BrandCard(child: child),
|
child: BrandCard(child: child),
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,7 +28,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
child:
|
child:
|
||||||
BrandHeader(title: 'more.settings'.tr(), hasBackButton: true),
|
BrandHeader(title: 'more.settings.title'.tr(), hasBackButton: true),
|
||||||
preferredSize: Size.fromHeight(52),
|
preferredSize: Size.fromHeight(52),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
|
@ -47,8 +47,8 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: _TextColumn(
|
child: _TextColumn(
|
||||||
title: 'Dark Theme',
|
title: 'more.settings.1'.tr(),
|
||||||
value: 'Change your the app theme',
|
value: 'more.settings.2'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
|
@ -75,8 +75,8 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: _TextColumn(
|
child: _TextColumn(
|
||||||
title: 'Reset app config',
|
title: 'more.settings.3'.tr(),
|
||||||
value: 'Reset api keys and root user',
|
value: 'more.settings.4'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
|
@ -85,7 +85,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
primary: BrandColors.red1,
|
primary: BrandColors.red1,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Reset',
|
'basis.reset'.tr(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: BrandColors.white,
|
color: BrandColors.white,
|
||||||
fontWeight: NamedFontWeight.demiBold,
|
fontWeight: NamedFontWeight.demiBold,
|
||||||
|
@ -96,11 +96,11 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BrandAlert(
|
return BrandAlert(
|
||||||
title: 'Вы уверенны',
|
title: 'modals.3'.tr(),
|
||||||
contentText: 'Сбросить все ключи?',
|
contentText: 'modals.4'.tr(),
|
||||||
acitons: [
|
acitons: [
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'Да, сбросить',
|
text: 'modals.5'.tr(),
|
||||||
isRed: true,
|
isRed: true,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context
|
context
|
||||||
|
@ -109,7 +109,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}),
|
}),
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'Отмена',
|
text: 'basis.cancel'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -39,7 +39,7 @@ class MorePage extends StatelessWidget {
|
||||||
goTo: InitializingPage(),
|
goTo: InitializingPage(),
|
||||||
),
|
),
|
||||||
_NavItem(
|
_NavItem(
|
||||||
title: 'more.settings'.tr(),
|
title: 'more.settings.title'.tr(),
|
||||||
iconData: BrandIcons.settings,
|
iconData: BrandIcons.settings,
|
||||||
goTo: AppSettingsPage(),
|
goTo: AppSettingsPage(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -124,8 +124,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<AppSettingsCubit>().turnOffOnboarding();
|
context.read<AppSettingsCubit>().turnOffOnboarding();
|
||||||
Navigator.of(context)
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
.pushReplacement(materialRoute(widget.nextPage));
|
materialRoute(widget.nextPage),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
title: 'basis.got_it'.tr(),
|
title: 'basis.got_it'.tr(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,9 +10,14 @@ import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.da
|
||||||
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/components/icon_status_mask/icon_status_mask.dart';
|
||||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||||
import 'package:selfprivacy/ui/pages/providers/settings/settings.dart';
|
import 'package:selfprivacy/ui/components/one_page/one_page.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/server_details/server_details.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/utils/route_transitions/slide_bottom.dart';
|
||||||
|
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||||
|
|
||||||
|
var navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
class ProvidersPage extends StatefulWidget {
|
class ProvidersPage extends StatefulWidget {
|
||||||
ProvidersPage({Key? key}) : super(key: key);
|
ProvidersPage({Key? key}) : super(key: key);
|
||||||
|
@ -27,9 +32,17 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
||||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||||
|
|
||||||
final cards = ProviderType.values
|
final cards = ProviderType.values
|
||||||
.map((type) => _Card(
|
.map(
|
||||||
provider:
|
(type) => Padding(
|
||||||
ProviderModel(state: StateType.uninitialized, type: type)))
|
padding: EdgeInsets.only(bottom: 30),
|
||||||
|
child: _Card(
|
||||||
|
provider: ProviderModel(
|
||||||
|
state: isReady ? StateType.stable : StateType.uninitialized,
|
||||||
|
type: type,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList();
|
.toList();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
|
@ -56,9 +69,11 @@ class _Card extends StatelessWidget {
|
||||||
final ProviderModel provider;
|
final ProviderModel provider;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String? title;
|
late String title;
|
||||||
String? message;
|
String? message;
|
||||||
String? stableText;
|
late String stableText;
|
||||||
|
late VoidCallback onTap;
|
||||||
|
|
||||||
AppConfigState appConfig = context.watch<AppConfigCubit>().state;
|
AppConfigState appConfig = context.watch<AppConfigCubit>().state;
|
||||||
|
|
||||||
var domainName =
|
var domainName =
|
||||||
|
@ -67,30 +82,54 @@ class _Card extends StatelessWidget {
|
||||||
switch (provider.type) {
|
switch (provider.type) {
|
||||||
case ProviderType.server:
|
case ProviderType.server:
|
||||||
title = 'providers.server.card_title'.tr();
|
title = 'providers.server.card_title'.tr();
|
||||||
stableText = 'В норме';
|
stableText = 'providers.domain.status'.tr();
|
||||||
|
|
||||||
|
stableText = 'providers.server.status'.tr();
|
||||||
|
onTap = () => Navigator.of(context).push(
|
||||||
|
SlideBottomRoute(
|
||||||
|
OnePage(
|
||||||
|
title: title,
|
||||||
|
child: ServerDetails(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case ProviderType.domain:
|
case ProviderType.domain:
|
||||||
title = 'providers.domain.card_title'.tr();
|
title = 'providers.domain.card_title'.tr();
|
||||||
message = domainName;
|
message = domainName;
|
||||||
stableText = 'Домен настроен';
|
stableText = 'providers.domain.status'.tr();
|
||||||
|
|
||||||
|
onTap = () => showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _ProviderDetails(
|
||||||
|
provider: provider,
|
||||||
|
statusText: stableText,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case ProviderType.backup:
|
case ProviderType.backup:
|
||||||
title = 'providers.backup.card_title'.tr();
|
title = 'providers.backup.card_title'.tr();
|
||||||
stableText = 'В норме';
|
stableText = 'providers.backup.status'.tr();
|
||||||
|
|
||||||
|
onTap = () => showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _ProviderDetails(
|
||||||
|
provider: provider,
|
||||||
|
statusText: stableText,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => showModalBottomSheet<void>(
|
onTap: onTap,
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return _ProviderDetails(
|
|
||||||
provider: provider,
|
|
||||||
statusText: stableText,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
child: BrandCard(
|
child: BrandCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -131,20 +170,11 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
|
|
||||||
var config = context.watch<AppConfigCubit>().state;
|
var config = context.watch<AppConfigCubit>().state;
|
||||||
|
|
||||||
var domainName = config.isDomainFilled
|
var domainName = UiHelpers.getDomainName(config);
|
||||||
? config.cloudFlareDomain!.domainName!
|
|
||||||
: 'example.com';
|
|
||||||
switch (provider.type) {
|
switch (provider.type) {
|
||||||
case ProviderType.server:
|
case ProviderType.server:
|
||||||
title = 'providers.server.card_title'.tr();
|
throw ('wrong type');
|
||||||
children = [
|
|
||||||
BrandText.body1('providers.server.bottom_sheet.1'.tr()),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
BrandText.body1('providers.server.bottom_sheet.2'.tr()),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
BrandText.body1('providers.server.bottom_sheet.3'.tr()),
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case ProviderType.domain:
|
case ProviderType.domain:
|
||||||
title = 'providers.domain.card_title'.tr();
|
title = 'providers.domain.card_title'.tr();
|
||||||
children = [
|
children = [
|
||||||
|
@ -153,7 +183,7 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
BrandText.body1(
|
BrandText.body1(
|
||||||
'providers.domain.bottom_sheet.2'.tr(args: [domainName, 'Date'])),
|
'providers.domain.bottom_sheet.2'.tr(args: [domainName, 'Date'])),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body1('providers.domain.bottom_sheet.3'.tr()),
|
BrandText.body1('providers.domain.status'.tr()),
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case ProviderType.backup:
|
case ProviderType.backup:
|
||||||
|
@ -164,7 +194,7 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
BrandText.body1(
|
BrandText.body1(
|
||||||
'providers.backup.bottom_sheet.2'.tr(args: [domainName, 'Time'])),
|
'providers.backup.bottom_sheet.2'.tr(args: [domainName, 'Time'])),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body1('providers.backup.bottom_sheet.3'.tr()),
|
BrandText.body1('providers.backup.status'.tr()),
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -177,38 +207,7 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Align(
|
SizedBox(height: 40),
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: 4,
|
|
||||||
horizontal: 2,
|
|
||||||
),
|
|
||||||
child: PopupMenuButton<_PopupMenuItemType>(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
),
|
|
||||||
onSelected: (_PopupMenuItemType result) {
|
|
||||||
switch (result) {
|
|
||||||
case _PopupMenuItemType.setting:
|
|
||||||
navigatorKey.currentState!
|
|
||||||
.push(materialRoute(SettingsPage()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.more_vert),
|
|
||||||
itemBuilder: (BuildContext context) => [
|
|
||||||
PopupMenuItem<_PopupMenuItemType>(
|
|
||||||
value: _PopupMenuItemType.setting,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.only(left: 5),
|
|
||||||
child: Text('basis.settings'.tr()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: brandPagePadding2,
|
padding: brandPagePadding2,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -234,5 +233,3 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _PopupMenuItemType { setting }
|
|
||||||
|
|
|
@ -0,0 +1,287 @@
|
||||||
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.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:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart';
|
||||||
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
|
||||||
|
part 'server_settings.dart';
|
||||||
|
|
||||||
|
var navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
class ServerDetails extends StatefulWidget {
|
||||||
|
const ServerDetails({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ServerDetailsState createState() => _ServerDetailsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ServerDetailsState extends State<ServerDetails>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late TabController tabController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
tabController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
tabController = TabController(length: 2, vsync: this);
|
||||||
|
tabController.addListener(() {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||||
|
var providerState = isReady ? StateType.stable : StateType.uninitialized;
|
||||||
|
|
||||||
|
late String title = 'providers.server.card_title'.tr();
|
||||||
|
|
||||||
|
return TabBarView(
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
controller: tabController,
|
||||||
|
children: [
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => ServerDetailsCubit()..check(),
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
var details = context.watch<ServerDetailsCubit>().state;
|
||||||
|
if (details is ServerDetailsLoading ||
|
||||||
|
details is ServerDetailsInitial) {
|
||||||
|
return _TempMessage(message: 'basis.loading'.tr());
|
||||||
|
} else if (details is ServerDetailsNotReady) {
|
||||||
|
return _TempMessage(message: 'basis.no_data'.tr());
|
||||||
|
} else if (details is Loaded) {
|
||||||
|
var data = details.serverInfo;
|
||||||
|
var checkTime = details.checkTime;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: brandPagePadding2,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconStatusMask(
|
||||||
|
status: providerState,
|
||||||
|
child: Icon(
|
||||||
|
BrandIcons.server,
|
||||||
|
size: 40,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
BrandText.h2(title),
|
||||||
|
Spacer(),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 4,
|
||||||
|
horizontal: 2,
|
||||||
|
),
|
||||||
|
child: PopupMenuButton<_PopupMenuItemType>(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
onSelected: (_PopupMenuItemType result) {
|
||||||
|
switch (result) {
|
||||||
|
case _PopupMenuItemType.setting:
|
||||||
|
tabController.animateTo(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.more_vert),
|
||||||
|
itemBuilder: (BuildContext context) => [
|
||||||
|
PopupMenuItem<_PopupMenuItemType>(
|
||||||
|
value: _PopupMenuItemType.setting,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 5),
|
||||||
|
child: Text('basis.settings'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
BrandText.body1('providers.server.bottom_sheet.1'.tr()),
|
||||||
|
SizedBox(height: 30),
|
||||||
|
Center(child: BrandText.h2('providers.server.2'.tr())),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: FractionColumnWidth(.5),
|
||||||
|
1: FractionColumnWidth(.5),
|
||||||
|
},
|
||||||
|
defaultVerticalAlignment:
|
||||||
|
TableCellVerticalAlignment.middle,
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Last check'),
|
||||||
|
getRowValue(formater.format(checkTime)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Server Id'),
|
||||||
|
getRowValue(data.id.toString()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Status:'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.status.toString().split('.')[1].toUpperCase()}',
|
||||||
|
isBold: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('CPU'),
|
||||||
|
getRowValue(
|
||||||
|
data.serverType.cores.toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Memory'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.serverType.memory.toString()} GB',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Disk Local'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.serverType.disk.toString()} GB',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Price monthly:'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.serverType.prices[1].monthly.toString()}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Price hourly:'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.serverType.prices[1].hourly.toString()}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 30),
|
||||||
|
Center(child: BrandText.h2('providers.server.3'.tr())),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: FractionColumnWidth(.5),
|
||||||
|
1: FractionColumnWidth(.5),
|
||||||
|
},
|
||||||
|
defaultVerticalAlignment:
|
||||||
|
TableCellVerticalAlignment.middle,
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Country'),
|
||||||
|
getRowValue(
|
||||||
|
'${data.location.country}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('City'),
|
||||||
|
getRowValue(data.location.city),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
getRowTitle('Description'),
|
||||||
|
getRowValue(data.location.description),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw Exception('wrong state');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_ServerSettings(tabController: tabController),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getRowTitle(String title) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 10),
|
||||||
|
child: BrandText.h5(
|
||||||
|
title,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getRowValue(String title, {bool isBold = false}) {
|
||||||
|
return BrandText.body1(
|
||||||
|
title,
|
||||||
|
style: isBold
|
||||||
|
? TextStyle(
|
||||||
|
fontWeight: NamedFontWeight.demiBold,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _PopupMenuItemType { setting }
|
||||||
|
|
||||||
|
class _TempMessage extends StatelessWidget {
|
||||||
|
const _TempMessage({
|
||||||
|
Key? key,
|
||||||
|
required this.message,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height - 100,
|
||||||
|
child: Center(
|
||||||
|
child: BrandText.body2(message),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateFormat formater = DateFormat('HH:mm:ss');
|
|
@ -0,0 +1,136 @@
|
||||||
|
part of 'server_details.dart';
|
||||||
|
|
||||||
|
class _ServerSettings extends StatelessWidget {
|
||||||
|
const _ServerSettings({
|
||||||
|
Key? key,
|
||||||
|
required this.tabController,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final TabController tabController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
padding: brandPagePadding2,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Container(
|
||||||
|
height: 52,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
padding: EdgeInsets.only(left: 1),
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(BrandIcons.arrow_left),
|
||||||
|
onPressed: () => tabController.animateTo(0),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
BrandText.h4('basis.settings'.tr()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BrandDivider(),
|
||||||
|
SwitcherBlock(
|
||||||
|
onChange: (_) {},
|
||||||
|
child: _TextColumn(
|
||||||
|
title: 'Allow Auto-upgrade',
|
||||||
|
value: 'Wether to allow automatic packages upgrades',
|
||||||
|
),
|
||||||
|
isActive: true,
|
||||||
|
),
|
||||||
|
SwitcherBlock(
|
||||||
|
onChange: (_) {},
|
||||||
|
child: _TextColumn(
|
||||||
|
title: 'Reboot after upgrade',
|
||||||
|
value: 'Reboot without prompt after applying updates',
|
||||||
|
),
|
||||||
|
isActive: false,
|
||||||
|
),
|
||||||
|
_Button(
|
||||||
|
onTap: () {},
|
||||||
|
child: _TextColumn(
|
||||||
|
title: 'Server Timezone',
|
||||||
|
value: 'Europe/Kyiv',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_Button(
|
||||||
|
onTap: () {},
|
||||||
|
child: _TextColumn(
|
||||||
|
title: 'Server Locale',
|
||||||
|
value: 'Default',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_Button(
|
||||||
|
onTap: () {},
|
||||||
|
child: _TextColumn(
|
||||||
|
hasWarning: true,
|
||||||
|
title: 'Factory Reset',
|
||||||
|
value: 'Restore default settings on your server',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Button extends StatelessWidget {
|
||||||
|
const _Button({
|
||||||
|
Key? key,
|
||||||
|
required this.onTap,
|
||||||
|
required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(top: 20, bottom: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
|
||||||
|
)),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextColumn extends StatelessWidget {
|
||||||
|
const _TextColumn({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
this.hasWarning = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String value;
|
||||||
|
final bool hasWarning;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
BrandText.body1(
|
||||||
|
title,
|
||||||
|
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
BrandText.body1(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
height: 1.53,
|
||||||
|
color: hasWarning ? BrandColors.warning : BrandColors.gray1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,15 +4,15 @@ import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
import 'package:selfprivacy/config/text_themes.dart';
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.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_card/brand_card.dart';
|
import 'package:selfprivacy/ui/components/brand_card/brand_card.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_modal_sheet/brand_modal_sheet.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/components/icon_status_mask/icon_status_mask.dart';
|
||||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../rootRoute.dart';
|
import '../rootRoute.dart';
|
||||||
|
@ -40,7 +40,14 @@ class _ServicesPageState extends State<ServicesPage> {
|
||||||
BrandText.body1('services.title'.tr()),
|
BrandText.body1('services.title'.tr()),
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
if (!isReady) ...[NotReadyCard(), SizedBox(height: 24)],
|
if (!isReady) ...[NotReadyCard(), SizedBox(height: 24)],
|
||||||
...ServiceTypes.values.map((t) => _Card(serviceType: t)).toList()
|
...ServiceTypes.values
|
||||||
|
.map((t) => Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: 30,
|
||||||
|
),
|
||||||
|
child: _Card(serviceType: t),
|
||||||
|
))
|
||||||
|
.toList()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -98,10 +105,10 @@ class _Card extends StatelessWidget {
|
||||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||||
var changeTab = context.read<ChangeTab>().onPress;
|
var changeTab = context.read<ChangeTab>().onPress;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => showModalBottomSheet<void>(
|
onTap: () => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
// isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
// backgroundColor: Colors.transparent,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return _ServiceDetails(
|
return _ServiceDetails(
|
||||||
serviceType: serviceType,
|
serviceType: serviceType,
|
||||||
|
@ -163,9 +170,7 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
late Widget child;
|
late Widget child;
|
||||||
|
|
||||||
var config = context.watch<AppConfigCubit>().state;
|
var config = context.watch<AppConfigCubit>().state;
|
||||||
var domainName = config.isDomainFilled
|
var domainName = UiHelpers.getDomainName(config);
|
||||||
? config.cloudFlareDomain!.domainName!
|
|
||||||
: 'example.com';
|
|
||||||
|
|
||||||
var linksStyle = body1Style.copyWith(
|
var linksStyle = body1Style.copyWith(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
|
@ -174,7 +179,6 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
: BrandColors.black,
|
: BrandColors.black,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
// height: 1.1,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var textStyle = body1Style.copyWith(
|
var textStyle = body1Style.copyWith(
|
||||||
|
@ -191,9 +195,10 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]),
|
text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Text(
|
child: Text(
|
||||||
'services.mail.bottom_sheet.2'.tr(),
|
'services.mail.bottom_sheet.2'.tr(),
|
||||||
|
@ -229,9 +234,10 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
.tr(args: [domainName]),
|
.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _launchURL('https://password.$domainName'),
|
onTap: () => _launchURL('https://password.$domainName'),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -252,9 +258,10 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
text: 'services.video.bottom_sheet.1'.tr(args: [domainName]),
|
text: 'services.video.bottom_sheet.1'.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _launchURL('https://meet.$domainName'),
|
onTap: () => _launchURL('https://meet.$domainName'),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -275,9 +282,10 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]),
|
text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _launchURL('https://cloud.$domainName'),
|
onTap: () => _launchURL('https://cloud.$domainName'),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -299,11 +307,12 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
.tr(args: [domainName]),
|
.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _launchURL('https://social_network.$domainName'),
|
onTap: () => _launchURL('https://social.$domainName'),
|
||||||
child: Text(
|
child: Text(
|
||||||
'social.$domainName',
|
'social.$domainName',
|
||||||
style: linksStyle,
|
style: linksStyle,
|
||||||
|
@ -322,9 +331,10 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
text: 'services.git.bottom_sheet.1'.tr(args: [domainName]),
|
text: 'services.git.bottom_sheet.1'.tr(args: [domainName]),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
|
WidgetSpan(child: SizedBox(width: 5)),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
padding: EdgeInsets.only(bottom: 0.8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _launchURL('https://git.$domainName'),
|
onTap: () => _launchURL('https://git.$domainName'),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -338,39 +348,312 @@ class _ServiceDetails extends StatelessWidget {
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return BrandModalSheet(
|
return Dialog(
|
||||||
child: Navigator(
|
shape: RoundedRectangleBorder(
|
||||||
key: navigatorKey,
|
borderRadius: BorderRadius.circular(20),
|
||||||
initialRoute: '/',
|
),
|
||||||
onGenerateRoute: (_) {
|
child: SingleChildScrollView(
|
||||||
return materialRoute(
|
child: Container(
|
||||||
Column(
|
width: 350,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Padding(
|
children: [
|
||||||
padding: brandPagePadding1,
|
Padding(
|
||||||
child: Column(
|
padding: brandPagePadding1,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
SizedBox(height: 13),
|
children: [
|
||||||
IconStatusMask(
|
IconStatusMask(
|
||||||
status: status,
|
status: status,
|
||||||
child: Icon(icon, size: 40, color: Colors.white),
|
child: Icon(icon, size: 40, color: Colors.white),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
BrandText.h2(title),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
child,
|
||||||
|
SizedBox(height: 40),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
child: BrandButton.rised(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
title: 'basis.close'.tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
),
|
||||||
BrandText.h1(title),
|
],
|
||||||
child,
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _launchURL(url) async =>
|
void _launchURL(url) async {
|
||||||
await canLaunch(url) ? await launch(url) : throw 'Could not launch $url';
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,12 @@ class _NewUser extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var config = context.watch<AppConfigCubit>().state;
|
var config = context.watch<AppConfigCubit>().state;
|
||||||
|
|
||||||
var domainName = config.isDomainFilled
|
var domainName = UiHelpers.getDomainName(config);
|
||||||
? config.cloudFlareDomain!.domainName!
|
|
||||||
: 'example.com';
|
|
||||||
|
|
||||||
return BrandModalSheet(
|
return BrandModalSheet(
|
||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (context) =>
|
create: (context) =>
|
||||||
UserFormCubit(usersCubit: context.watch<UsersCubit>()),
|
UserFormCubit(usersCubit: context.read<UsersCubit>()),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var formCubitState = context.watch<UserFormCubit>().state;
|
var formCubitState = context.watch<UserFormCubit>().state;
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,7 @@ class _UserDetails extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var config = context.watch<AppConfigCubit>().state;
|
var config = context.watch<AppConfigCubit>().state;
|
||||||
|
|
||||||
var domainName = config.isDomainFilled
|
var domainName = UiHelpers.getDomainName(config);
|
||||||
? config.cloudFlareDomain!.domainName!
|
|
||||||
: 'example.com';
|
|
||||||
|
|
||||||
return BrandModalSheet(
|
return BrandModalSheet(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -44,8 +42,8 @@ class _UserDetails extends StatelessWidget {
|
||||||
),
|
),
|
||||||
onSelected: (PopupMenuItemType result) {
|
onSelected: (PopupMenuItemType result) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case PopupMenuItemType.reset:
|
// case PopupMenuItemType.reset:
|
||||||
break;
|
// break;
|
||||||
case PopupMenuItemType.delete:
|
case PopupMenuItemType.delete:
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -88,13 +86,13 @@ class _UserDetails extends StatelessWidget {
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
itemBuilder: (BuildContext context) => [
|
itemBuilder: (BuildContext context) => [
|
||||||
PopupMenuItem<PopupMenuItemType>(
|
// PopupMenuItem<PopupMenuItemType>(
|
||||||
value: PopupMenuItemType.reset,
|
// value: PopupMenuItemType.reset,
|
||||||
child: Container(
|
// child: Container(
|
||||||
padding: EdgeInsets.only(left: 5),
|
// padding: EdgeInsets.only(left: 5),
|
||||||
child: Text('users.reset_password'.tr()),
|
// child: Text('users.reset_password'.tr()),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
PopupMenuItem<PopupMenuItemType>(
|
PopupMenuItem<PopupMenuItemType>(
|
||||||
value: PopupMenuItemType.delete,
|
value: PopupMenuItemType.delete,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -145,7 +143,7 @@ class _UserDetails extends StatelessWidget {
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
BrandDivider(),
|
BrandDivider(),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
BrandButton.iconText(
|
BrandButton.emptyWithIconText(
|
||||||
title: 'users.send_regisration_data'.tr(),
|
title: 'users.send_regisration_data'.tr(),
|
||||||
icon: Icon(BrandIcons.share),
|
icon: Icon(BrandIcons.share),
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
|
@ -161,6 +159,6 @@ class _UserDetails extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PopupMenuItemType {
|
enum PopupMenuItemType {
|
||||||
reset,
|
// reset,
|
||||||
delete,
|
delete,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.da
|
||||||
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/not_ready_card/not_ready_card.dart';
|
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||||
|
|
||||||
part 'fab.dart';
|
part 'fab.dart';
|
||||||
part 'new_user.dart';
|
part 'new_user.dart';
|
||||||
|
|
|
@ -4,11 +4,11 @@ const _chars =
|
||||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_';
|
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_';
|
||||||
Random _rnd = Random();
|
Random _rnd = Random();
|
||||||
|
|
||||||
String getRandomString(int length) => String.fromCharCodes(
|
String getRandomString(int length, [chars = _chars]) => String.fromCharCodes(
|
||||||
Iterable.generate(
|
Iterable.generate(
|
||||||
length,
|
length,
|
||||||
(_) => _chars.codeUnitAt(
|
(_) => chars.codeUnitAt(
|
||||||
_rnd.nextInt(_chars.length),
|
_rnd.nextInt(chars.length),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,8 +36,10 @@ Function transitionsBuilder = (
|
||||||
class SlideBottomRoute extends PageRouteBuilder {
|
class SlideBottomRoute extends PageRouteBuilder {
|
||||||
SlideBottomRoute(this.widget)
|
SlideBottomRoute(this.widget)
|
||||||
: super(
|
: super(
|
||||||
|
transitionDuration: Duration(milliseconds: 150),
|
||||||
pageBuilder: pageBuilder(widget),
|
pageBuilder: pageBuilder(widget),
|
||||||
transitionsBuilder: transitionsBuilder as Widget Function(BuildContext, Animation<double>, Animation<double>, Widget),
|
transitionsBuilder: transitionsBuilder as Widget Function(
|
||||||
|
BuildContext, Animation<double>, Animation<double>, Widget),
|
||||||
);
|
);
|
||||||
|
|
||||||
final Widget widget;
|
final Widget widget;
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
|
|
||||||
|
/// it's ui helpers use only for ui components, don't use for logic components.
|
||||||
|
|
||||||
|
class UiHelpers {
|
||||||
|
static String getDomainName(AppConfigState config) => config.isDomainFilled
|
||||||
|
? config.cloudFlareDomain!.domainName
|
||||||
|
: 'example.com';
|
||||||
|
}
|
122
pubspec.lock
122
pubspec.lock
|
@ -7,14 +7,14 @@ packages:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.0.0"
|
version: "19.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.41.2"
|
version: "1.3.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -42,7 +42,7 @@ packages:
|
||||||
name: basic_utils
|
name: basic_utils
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0-nullsafety.1"
|
version: "3.0.0-nullsafety.3"
|
||||||
bloc:
|
bloc:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -63,14 +63,14 @@ packages:
|
||||||
name: build
|
name: build
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.2"
|
version: "2.0.0"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.5"
|
version: "0.4.7"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -84,35 +84,35 @@ packages:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.3"
|
version: "2.0.0"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.5"
|
version: "1.12.2"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.10"
|
version: "6.1.12"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_collection
|
name: built_collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.2"
|
version: "5.0.0"
|
||||||
built_value:
|
built_value:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.1.0"
|
version: "8.0.4"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -133,14 +133,14 @@ packages:
|
||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.1"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.3.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -154,7 +154,7 @@ packages:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.7.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -175,7 +175,7 @@ packages:
|
||||||
name: coverage
|
name: coverage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.2"
|
version: "1.0.2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -189,7 +189,7 @@ packages:
|
||||||
name: cubit_form
|
name: cubit_form
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0-nullsafety.0"
|
version: "1.0.2-nullsafety.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -203,14 +203,14 @@ packages:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.12"
|
version: "2.0.0"
|
||||||
dio:
|
dio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0-prev1"
|
version: "4.0.0"
|
||||||
easy_localization:
|
easy_localization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -273,7 +273,7 @@ packages:
|
||||||
name: fixnum
|
name: fixnum
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.10.11"
|
version: "1.0.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -304,7 +304,7 @@ packages:
|
||||||
name: flutter_markdown
|
name: flutter_markdown
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.1"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -335,21 +335,21 @@ packages:
|
||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.1"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "1.0.0"
|
||||||
hive:
|
hive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: hive
|
name: hive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.1"
|
||||||
hive_flutter:
|
hive_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -357,20 +357,27 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
hive_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: hive_generator
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.0"
|
version: "0.13.1"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_multi_server
|
name: http_multi_server
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "3.0.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -384,7 +391,7 @@ packages:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -398,7 +405,7 @@ packages:
|
||||||
name: io
|
name: io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.4"
|
version: "1.0.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -412,21 +419,21 @@ packages:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.1"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
markdown:
|
markdown:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -461,7 +468,7 @@ packages:
|
||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.7"
|
version: "1.0.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -482,7 +489,7 @@ packages:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.3"
|
version: "2.0.0"
|
||||||
package_info:
|
package_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -566,7 +573,7 @@ packages:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0-nullsafety.2"
|
version: "3.0.1"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -574,13 +581,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
|
pretty_dio_logger:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pretty_dio_logger
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0-beta-1"
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: process
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.2.1"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -594,21 +608,14 @@ packages:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.4"
|
version: "2.0.0"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.7"
|
version: "1.0.0"
|
||||||
quiver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: quiver
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.5"
|
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -664,21 +671,21 @@ packages:
|
||||||
name: shelf_packages_handler
|
name: shelf_packages_handler
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
shelf_static:
|
shelf_static:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_static
|
name: shelf_static
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.9+2"
|
version: "1.0.0"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.4+1"
|
version: "1.0.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -690,7 +697,7 @@ packages:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.10+3"
|
version: "1.0.0"
|
||||||
source_map_stack_trace:
|
source_map_stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -732,7 +739,7 @@ packages:
|
||||||
name: stream_transform
|
name: stream_transform
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "2.0.0"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -774,7 +781,7 @@ packages:
|
||||||
name: timing
|
name: timing
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1+3"
|
version: "1.0.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -782,13 +789,20 @@ 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: "1.0.2"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.3"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -837,7 +851,7 @@ packages:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.0"
|
version: "6.1.0+1"
|
||||||
wakelock:
|
wakelock:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -879,28 +893,28 @@ packages:
|
||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.7+15"
|
version: "1.0.0"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "2.0.0"
|
||||||
webkit_inspection_protocol:
|
webkit_inspection_protocol:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webkit_inspection_protocol
|
name: webkit_inspection_protocol
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.5"
|
version: "1.0.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.5"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: selfprivacy
|
name: selfprivacy
|
||||||
description: selfprivacy.org
|
description: selfprivacy.org
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.1.0+1
|
version: 0.1.0+2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.12.0 <3.0.0'
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
@ -17,7 +17,7 @@ dependencies:
|
||||||
easy_localization: ^3.0.0
|
easy_localization: ^3.0.0
|
||||||
either_option: ^2.0.1-dev.1
|
either_option: ^2.0.1-dev.1
|
||||||
equatable: ^2.0.0
|
equatable: ^2.0.0
|
||||||
flutter_bloc: ^7.0.0-nullsafety.5
|
flutter_bloc: ^7.0.0
|
||||||
flutter_markdown: ^0.6.0
|
flutter_markdown: ^0.6.0
|
||||||
flutter_secure_storage: ^4.1.0
|
flutter_secure_storage: ^4.1.0
|
||||||
get_it: ^6.0.0
|
get_it: ^6.0.0
|
||||||
|
@ -25,7 +25,9 @@ dependencies:
|
||||||
hive_flutter: ^1.0.0
|
hive_flutter: ^1.0.0
|
||||||
json_annotation: ^4.0.0
|
json_annotation: ^4.0.0
|
||||||
package_info: ^2.0.0
|
package_info: ^2.0.0
|
||||||
|
pretty_dio_logger: ^1.1.1
|
||||||
provider: ^5.0.0
|
provider: ^5.0.0
|
||||||
|
unicons: ^1.0.2
|
||||||
url_launcher: ^6.0.2
|
url_launcher: ^6.0.2
|
||||||
wakelock: ^0.5.0+2
|
wakelock: ^0.5.0+2
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ dev_dependencies:
|
||||||
basic_utils: ^3.0.0-nullsafety.1
|
basic_utils: ^3.0.0-nullsafety.1
|
||||||
build_runner: ^1.11.5
|
build_runner: ^1.11.5
|
||||||
flutter_launcher_icons: ^0.9.0
|
flutter_launcher_icons: ^0.9.0
|
||||||
|
hive_generator: ^1.0.0
|
||||||
json_serializable: ^4.0.2
|
json_serializable: ^4.0.2
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
|
|
Loading…
Reference in New Issue