Merge pull request 'translations' (#3) from translations into master

Reviewed-on: kherel/selfprivacy.org.app#3
fdroid
kherel 2021-03-18 09:38:29 +02:00
commit a59575b4d4
119 changed files with 1749 additions and 1127 deletions

2
.gitignore vendored
View File

@ -18,7 +18,7 @@
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
.vscode/
# Flutter/Dart/Pub related
**/doc/api/

13
.vscode/launch.json vendored
View File

@ -1,13 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "selfprivacy",
"request": "launch",
"type": "dart"
}
]
}

Binary file not shown.

BIN
assets/fonts/Inter-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@ -0,0 +1,12 @@
### О проекте
Всё больше организаций хотят владеть нашими данными
А мы сами хотим распоряжаться своими **данными** на своем сервере.
### Миссия проекта
Цифровая независимость и приватность доступная каждому
### Цель
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких

View File

@ -0,0 +1,12 @@
### О проекте
Всё больше организаций хотят владеть нашими данными
А мы сами хотим распоряжаться своими **данными** на своем сервере.
### Миссия проекта
Цифровая независимость и приватность доступная каждому
### Цель
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких

View File

@ -0,0 +1,7 @@
### Как получить Hetzner API Token
1. Переходим по ссылке https://hetzner.com
2. Заходим в созданный нами проект. Если такового - нет, значит создаём.
3. Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).
4. Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.
5. В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.
6. В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.

View File

@ -0,0 +1,7 @@
### Как получить Hetzner API Token
1. Переходим по ссылке https://hetzner.com
2. Заходим в созданный нами проект. Если такового - нет, значит создаём.
3. Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).
4. Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.
5. В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.
6. В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.

View File

@ -1,3 +1,171 @@
{
"test": "en-test"
"test": "en-test",
"locale": "en",
"basis": {
"_comment": "Basic interface elements",
"providers": "Providers",
"services": "Services",
"users": "Users",
"more": "More",
"next": "Next",
"got_it": "Got it",
"settings": "Settings",
"password": "Password",
"create": "Add new",
"confirmation": "Confirmation",
"cancel": "Cancel",
"delete": "Delete",
"close": "Close",
"connect": "Connect",
"domain": "Domain",
"saving": "Saving..",
"nickname": "nickname",
"loading": "loading"
},
"more": {
"_comment": "'More' tab",
"configuration_wizard": "Setup wizard",
"settings": "Application settings",
"about_project": "About us",
"about_app": "About application",
"onboarding": "Onboarding",
"console": "Console",
"about_app_page": {
"text": "Тут любая служебная информация, v.{}"
}
},
"onboarding": {
"_comment": "Onboarding pages",
"page1_title": "Digital independence, available to all of us",
"page1_text": "Mail, VPN, Messenger, social network and much more on your private server, under your control.",
"page2_title": "SelfPrivacy — it's not a cloud, but your personal datacenter",
"page2_text": "SelfPrivacy works only with your provider accounts: Hetzner, Cloudflare, Backblaze. If you do not own those, we'll help you to create them"
},
"providers": {
"_comment": "'Providers' tab",
"page_title": "Your Data Center",
"server": {
"card_title": "Server",
"bottom_sheet": {
"1": "It's a virtual computer, where all your services live.",
"2": "1 CPU, RAM 4Gb, 40Gb — $5 per month",
"3": "Status — Good"
}
},
"domain": {
"card_title": "Domain",
"bottom_sheet": {
"1": "It's your personal internet address that will point to the server and other services of yours.",
"2": "{} — expires on {}",
"3": "Status — Good"
}
},
"backup": {
"card_title": "Backup",
"bottom_sheet": {
"1": "Will save your day in case of incident: hackers attack, server deletion, etc.",
"2": "3Gb/10Gb, last backup was yesterday {}",
"3": "Status — Good"
}
}
},
"not_ready_card": {
"_comment": "Card shown when user skips initial setup",
"1": "Please finish application setup using ",
"2": "@:more.configuration_wizard",
"3": " for further work"
},
"services": {
"_comment": "Вкладка сервисы",
"title": "Your personal, private and independent services.",
"mail": {
"title": "E-Mail",
"subtitle": "E-Mail for company and family.",
"bottom_sheet": {
"1": "To connect to the mailserver, please use {} domain alongside with username and password, that you created. Also feel free to invite",
"2": "new users"
}
},
"messenger": {
"title": "Messenger",
"subtitle": "Telegram or Signal not so private as Delta.Chat that uses your private server.",
"bottom_sheet": {
"1": "For connection, please use {} domain and credentials that you created."
}
},
"password_manager": {
"title": "Password Manager",
"subtitle": "Base of your security. Bitwarden will help you to create, store and move passwords between devices, as well as input them, when requested using autocompletion.",
"bottom_sheet": {
"1": "You can connect to the service and create a user via this link:"
}
},
"video": {
"title": "Videomeet",
"subtitle": "Zoom and Google Meet are good, but Jitsi Meet is a worth alternative that also gives you confidence that you're not being listened.",
"bottom_sheet": {
"1": "Using Jitsi as simple as just visiting this link:"
}
},
"cloud": {
"title": "Cloud Storage",
"subtitle": "Do not allow cloud services to read your data by using NextCloud.",
"bottom_sheet": {
"1": "You can connect and create a new user here:"
}
},
"social_network": {
"title": "Social Network",
"subtitle": "It's hard to believe, but it became possible to create your own social network, with your own rules and target audience.",
"bottom_sheet": {
"1": "You can connect and create new social user here:"
}
},
"git": {
"title": "Git-server",
"subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.",
"bottom_sheet": {
"1": "You can connect and create a new user here:"
}
}
},
"users": {
"_comment": "'Users' tab",
"add_new_user": "Добавьте первого пользователя",
"new_user": "Новый пользователь",
"not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя",
"nobody_here": "'Здесь пока никого'",
"login": "Логин",
"onboarding": "Onboarding",
"console": "Console",
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.",
"delete_confirm_question": "удалить учетную запись?",
"reset_password": "Сбросить пароль",
"account": "Учетная запись",
"send_regisration_data": "Отправить реквизиты для входа"
},
"initializing": {
"_comment": "initializing page",
"1": "Подключите сервер Hetzner",
"2": "Здесь будут жить наши данные и SelfPrivacy-сервисы",
"how": "Как получить API Token",
"3": "'Подключите CloudFlare'",
"4": "Для управления DNS вашего домена",
"5": "CloudFlare API Token",
"6": "Подключите облачное хранилище Backblaze",
"7": "На данный момент подлюченных доменов нет",
"8": "Загружаем список доменов",
"9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены",
"10": "Сохранить домен",
"11": "Создать сервер",
"what": "Что это значит?",
"13": "Сервер презагружен, ждем последнюю проверку",
"14": "Cервер запушен, сейчас он будет проверен и перезагружен",
"15": "Cервер создан, идет проверка ДНС адресов и запуск сервера",
"16": "До следующей проверки: ",
"17": "Проверка",
"18": "Как получить Hetzner API Token'",
"19": "1 Переходим по ссылке ",
"20": "\n"
}
}

View File

@ -1,3 +1,171 @@
{
"test": "ру-тест"
"test": "ru-test",
"locale": "ru",
"basis": {
"_comment": "базовые элементы интерфейса",
"providers": "Провайдеры",
"services": "Сервисы",
"users": "Пользователи",
"more": "Еще",
"next": "Далее",
"got_it": "Понял",
"settings": "Настройки",
"password": "Пароль",
"create": "Создать",
"confirmation": "Подтверждение",
"cancel": "Отменить",
"delete": "Удалить",
"close": "Закрыть",
"connect": "Подключить",
"domain": "Домен",
"saving": "Сохранение..",
"nickname": "Никнейм",
"loading": "Загрузка"
},
"more": {
"_comment": "вкладка еще",
"configuration_wizard": "Мастер Подключения",
"settings": "Настройки приложения",
"about_project": "О проекте SelfPrivacy",
"about_app": "О приложении",
"onboarding": "Onboarding",
"console": "Console",
"about_app_page": {
"text": "Тут любая служебная информация, v.{}"
}
},
"onboarding": {
"_comment": "страницы онбординга",
"page1_title": "Цифровая независимость доступна каждому",
"page1_text": "Почта, VPN, Мессенджер, социальная сеть и многое другое на вашем личном сервере, под вашим полным контролем.",
"page2_title": "SelfPrivacy — это не облако, а ваш личный дата-центр",
"page2_text": "У SelfPrivacy работает только с вашими сервис-провадерами: Hetzner, Cloudflare, Backblaze. Если у вас нет учетных записей, мы поможем их создать."
},
"providers": {
"_comment": "вкладка провайдеры",
"page_title": "Ваш Дата-центр",
"server": {
"card_title": "Сервер",
"bottom_sheet": {
"1": "Это виртульный компьютер на котором работают все ваши сервисы.",
"2": "1 CPU, RAM 4Gb, 40Gb — $5 в месяц",
"3": "Статус — в норме"
}
},
"domain": {
"card_title": "Домен",
"bottom_sheet": {
"1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.",
"2": "{} — продлен до {}",
"3": "Статус — в норме"
}
},
"backup": {
"card_title": "Резервное копирование",
"bottom_sheet": {
"1": "Выручит в любой ситуации: хакерская атака, удаление сервера и т.п.",
"2": "3Gb — бестплатно до 10Gb, последний вчера в {}",
"3": "Статус — в норме"
}
}
},
"not_ready_card": {
"_comment": "Карточка показывающая когда человек скипнул настройку, на карте текст из 3 блоков, средний содержит ссыку на мастер подключения",
"1": "Завершите настройку приложения используя ",
"2": "@:more.configuration_wizard",
"3": " для продолжения работы"
},
"services": {
"_comment": "Вкладка сервисы",
"title": "Ваши личные приватные и независимые сервисы",
"mail": {
"title": "Почта",
"subtitle": "Электронная почта для семьи или компании",
"bottom_sheet": {
"1": "Для подключения почтового ящика используйте {} и логин пароль, который вы создали. Так же приглашайте",
"2": "новых пользователей"
}
},
"messenger": {
"title": "Мессенджер",
"subtitle": "Telegram и Signal не так приватны, как Delta.Chat использующий ваш личный сервер.",
"bottom_sheet": {
"1": "Для подключения используйте {} и логин пароль, который вы создали"
}
},
"password_manager": {
"title": "Менеджер паролей",
"subtitle": "Фундамент безопасности. Создавать, хранить, копировать пароли между устройствами, вбивать их в формы поможет — Bitwarden.",
"bottom_sheet": {
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
}
},
"video": {
"title": "Видеоконференция",
"subtitle": "Zoom и Google meet отличные инструменты, но Jitsi meet не хуже и дает уверенность, что вас никто не подслушивает.",
"bottom_sheet": {
"1": "Для использования просто перейдите по ссылке:"
}
},
"cloud": {
"title": "Файловое облако",
"subtitle": "Не позволяйте облачным сервисам читать ваши данные используйте NextCloud.",
"bottom_sheet": {
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
}
},
"social_network": {
"title": "Социальная сеть",
"subtitle": "Сложно поверить, но стало возможным создать свою собственную социальную сеть, со своими правилами и аудиторией.",
"bottom_sheet": {
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
}
},
"git": {
"title": "Git-сервер",
"subtitle": "Приватная альтернатива Github, которая принадлежит вам, а не Microsoft.",
"bottom_sheet": {
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
}
}
},
"users": {
"_comment": "'Users' tab",
"add_new_user": "Добавьте первого пользователя",
"new_user": "Новый пользователь",
"not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя",
"nobody_here": "'Здесь пока никого'",
"login": "Логин",
"onboarding": "Onboarding",
"console": "Console",
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.",
"delete_confirm_question": "удалить учетную запись?",
"reset_password": "Сбросить пароль",
"account": "Учетная запись",
"send_regisration_data": "Отправить реквизиты для входа"
},
"initializing": {
"_comment": "initializing page",
"1": "Подключите сервер Hetzner",
"2": "Здесь будут жить наши данные и SelfPrivacy-сервисы",
"how": "Как получить API Token",
"3": "'Подключите CloudFlare'",
"4": "Для управления DNS вашего домена",
"5": "CloudFlare API Token",
"6": "Подключите облачное хранилище Backblaze",
"7": "На данный момент подлюченных доменов нет",
"8": "Загружаем список доменов",
"9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены",
"10": "Сохранить домен",
"11": "Создать сервер",
"what": "Что это значит?",
"13": "Сервер презагружен, ждем последнюю проверку",
"14": "Cервер запушен, сейчас он будет проверен и перезагружен",
"15": "Cервер создан, идет проверка ДНС адресов и запуск сервера",
"16": "До следующей проверки: ",
"17": "Проверка",
"18": "Как получить Hetzner API Token'",
"19": "1 Переходим по ссылке ",
"20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет."
}
}

View File

@ -0,0 +1,16 @@
SelfPrivacy - is a platform on your cloud hosting, that allows to deploy your own private services and control them using mobile application.
To use this application, you'll be required to create accounts of different service providers. Please reffer to this manual: https://hugo.selfprivacy.org/posts/getting_started
Application will do the following things for you:
1. Create your personal server
2. Setup NixOS
3. Bring all services to the ready-to-use state. Services include:
* E-mail, ready to use with DeltaChat
* NextCloud - your personal cloud storage
* Bitwarden - secure and private password manager
* Pleroma - your private fediverse space for blogging
* Jitsi — awesome Zoom alternative
* Gitea - your own Git server
* OpenConnect - Personal VPN server
!!! Project is currently in alpha state. Feel free to try it. It would be much appreciated if you would provide us with some feedback.
!!! Only Russian localization is currently available. English is coming soon

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

@ -0,0 +1 @@
Self-hosted services without pain

View File

@ -0,0 +1 @@
SelfPrivacy

View File

@ -0,0 +1 @@
https://fdroid.selfprivacy.org/SFPresentation.mp4

View File

@ -39,13 +39,13 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock/ios"
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
wakelock: bfc7955c418d0db797614075aabbc58a39ab5107
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

Binary file not shown.

View File

@ -3,13 +3,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
class BlocAndProviderConfig extends StatelessWidget {
const BlocAndProviderConfig({Key key, this.child}) : super(key: key);
const BlocAndProviderConfig({Key? key, this.child}) : super(key: key);
final Widget child;
final Widget? child;
@override
Widget build(BuildContext context) {
@ -30,7 +29,6 @@ class BlocAndProviderConfig extends StatelessWidget {
lazy: false,
create: (_) => AppConfigCubit()..load(),
),
BlocProvider(create: (_) => ServicesCubit()),
BlocProvider(create: (_) => ProvidersCubit()),
BlocProvider(create: (_) => UsersCubit()),
],

View File

@ -2,14 +2,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/ui/components/error/error.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'get_it_config.dart';
import './get_it_config.dart';
class SimpleBlocObserver extends BlocObserver {
SimpleBlocObserver();
@override
void onError(Cubit cubit, Object error, StackTrace stackTrace) {
final navigator = getIt.get<NavigationService>().navigator;
void onError(Bloc cubit, Object error, StackTrace stackTrace) {
final navigator = getIt.get<NavigationService>().navigator!;
navigator.push(
materialRoute(

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'brand_colors.dart';
final ligtTheme = ThemeData(
primaryColor: BrandColors.primary,
fontFamily: 'Inter',
brightness: Brightness.light,
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
inputDecorationTheme: InputDecorationTheme(
@ -33,21 +33,17 @@ final ligtTheme = ThemeData(
color: BrandColors.red1,
),
),
errorStyle: GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 12,
color: BrandColors.red1,
),
errorStyle: TextStyle(
fontSize: 12,
color: BrandColors.red1,
),
),
textTheme: GoogleFonts.interTextTheme(
TextTheme(
headline1: headline1Style,
headline2: headline2Style,
caption: headline4Style,
bodyText1: body1Style,
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
),
textTheme: TextTheme(
headline1: headline1Style,
headline2: headline2Style,
caption: headline4Style,
bodyText1: body1Style,
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
),
);
@ -57,14 +53,12 @@ var darkTheme = ligtTheme.copyWith(
iconTheme: IconThemeData(color: BrandColors.gray3),
cardColor: BrandColors.gray1,
dialogBackgroundColor: Color(0xFF202120),
textTheme: GoogleFonts.interTextTheme(
TextTheme(
headline1: headline1Style.copyWith(color: BrandColors.white),
headline2: headline2Style.copyWith(color: BrandColors.white),
caption: headline4Style.copyWith(color: BrandColors.white),
bodyText1: body1Style.copyWith(color: BrandColors.white),
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
),
textTheme: TextTheme(
headline1: headline1Style.copyWith(color: BrandColors.white),
headline2: headline2Style.copyWith(color: BrandColors.white),
caption: headline4Style.copyWith(color: BrandColors.white),
bodyText1: body1Style.copyWith(color: BrandColors.white),
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
),
inputDecorationTheme: InputDecorationTheme(
labelStyle: TextStyle(color: BrandColors.white),

View File

@ -32,7 +32,8 @@ class HiveConfig {
await secureStorage.write(key: BNames.key, value: base64UrlEncode(key));
}
return base64Url.decode(await secureStorage.read(key: BNames.key));
String? string = await secureStorage.read(key: BNames.key);
return base64Url.decode(string!);
}
}

View File

@ -3,20 +3,20 @@ import 'package:flutter/material.dart';
class Localization extends StatelessWidget {
const Localization({
Key key,
Key? key,
this.child,
}) : super(key: key);
final Widget child;
final Widget? child;
@override
Widget build(BuildContext context) {
return EasyLocalization(
preloaderColor: Colors.black,
supportedLocales: [Locale('ru'), Locale('en')],
path: 'assets/translations',
fallbackLocale: Locale('en'),
fallbackLocale: Locale('ru'),
saveLocale: false,
useOnlyLangCode: true,
child: child,
child: child!,
);
}
}

0
lib/config/md_files.dart Normal file
View File

View File

@ -1,41 +1,38 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
import 'brand_colors.dart';
final defaultTextStyle = GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 15,
color: BrandColors.textColor1,
),
final defaultTextStyle = TextStyle(
fontSize: 15,
color: BrandColors.textColor1,
);
final headline1Style = GoogleFonts.inter(
final headline1Style = defaultTextStyle.copyWith(
fontSize: 40,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final headline2Style = GoogleFonts.inter(
final headline2Style = defaultTextStyle.copyWith(
fontSize: 24,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final onboardingTitle = GoogleFonts.inter(
final onboardingTitle = defaultTextStyle.copyWith(
fontSize: 30,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final headline3Style = GoogleFonts.inter(
final headline3Style = defaultTextStyle.copyWith(
fontSize: 20,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final headline4Style = GoogleFonts.inter(
final headline4Style = defaultTextStyle.copyWith(
fontSize: 18,
fontWeight: NamedFontWeight.medium,
color: BrandColors.headlineColor,
@ -59,16 +56,12 @@ final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue);
final progressTextStyleLight = GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 11,
color: BrandColors.textColor1,
),
final progressTextStyleLight = TextStyle(
fontSize: 11,
color: BrandColors.textColor1,
);
final progressTextStyleDark = GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 11,
color: BrandColors.white,
),
final progressTextStyleDark = TextStyle(
fontSize: 11,
color: BrandColors.white,
);

View File

@ -18,9 +18,9 @@ abstract class ApiMap {
};
loggedClient = client;
}
String rootAddress;
String? rootAddress;
Dio loggedClient;
late Dio loggedClient;
void close() {
loggedClient.close();
@ -61,7 +61,7 @@ class ConsoleInterceptor extends InterceptorsWrapper {
addMessage(
Message.warn(
text:
'response-uri: ${response?.request?.uri}\ncode: ${response?.statusCode}\ndata: ${response?.toString()}\n',
'response-uri: ${response?.request.uri}\ncode: ${response?.statusCode}\ndata: ${response?.toString()}\n',
),
);
return super.onError(err);

View File

@ -3,17 +3,17 @@ import 'package:dio/dio.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart';
class BackblazeApi extends ApiMap {
BackblazeApi([String token]) {
BackblazeApi([String? token]) {
if (token != null) {
loggedClient.options = BaseOptions(
headers: {'Authorization': 'Basic $token'},
baseUrl: rootAddress,
baseUrl: rootAddress!,
);
}
}
@override
String rootAddress =
String? rootAddress =
'https://api.backblazeb2.com/b2api/v2/b2_authorize_account';
Future<bool> isValid(String token) async {
@ -24,7 +24,7 @@ class BackblazeApi extends ApiMap {
},
);
Response response = await loggedClient.get(rootAddress, options: options);
Response response = await loggedClient.get(rootAddress!, options: options);
if (response.statusCode == HttpStatus.ok) {
return true;

View File

@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
import 'package:selfprivacy/logic/models/dns_records.dart';
class CloudflareApi extends ApiMap {
CloudflareApi([String token]) {
CloudflareApi([String? token]) {
if (token != null) {
loggedClient.options =
BaseOptions(headers: {'Authorization': 'Bearer $token'});
@ -13,7 +13,7 @@ class CloudflareApi extends ApiMap {
}
@override
String rootAddress = 'https://api.cloudflare.com/client/v4';
String? rootAddress = 'https://api.cloudflare.com/client/v4';
Future<bool> isValid(String token) async {
var url = '$rootAddress/user/tokens/verify';
@ -35,7 +35,7 @@ class CloudflareApi extends ApiMap {
}
}
Future<String> getZoneId(String token, String domain) async {
Future<String?> getZoneId(String? token, String domain) async {
var url = '$rootAddress/zones';
var options = Options(
@ -59,8 +59,8 @@ class CloudflareApi extends ApiMap {
}
Future<void> removeSimilarRecords({
String ip4,
CloudFlareDomain cloudFlareDomain,
String? ip4,
required CloudFlareDomain cloudFlareDomain,
}) async {
var domainName = cloudFlareDomain.domainName;
var domainZoneId = cloudFlareDomain.zoneId;
@ -82,8 +82,8 @@ class CloudflareApi extends ApiMap {
}
Future<void> createMultipleDnsRecords({
String ip4,
CloudFlareDomain cloudFlareDomain,
String? ip4,
required CloudFlareDomain cloudFlareDomain,
}) async {
var domainName = cloudFlareDomain.domainName;
var domainZoneId = cloudFlareDomain.zoneId;
@ -120,7 +120,7 @@ class CloudflareApi extends ApiMap {
// );
// }
List<DnsRecords> projectDnsRecords(String domainName, String ip4) {
List<DnsRecords> projectDnsRecords(String? domainName, String? ip4) {
var domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
var mx = DnsRecords(type: 'MX', name: '@', content: domainName);
@ -161,7 +161,7 @@ class CloudflareApi extends ApiMap {
];
}
Future<List<String>> domainList() async {
Future<List<String>?> domainList() async {
var url = '$rootAddress/zones?per_page=50';
var response = await loggedClient.get(
url,
@ -169,7 +169,7 @@ class CloudflareApi extends ApiMap {
);
return response.data['result']
.map<String>((el) => el['name'] as String)
.map<String>((el) => el['name'] as String?)
.toList();
}
}

View File

@ -2,24 +2,23 @@ import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart';
import 'package:selfprivacy/logic/models/server_details.dart';
import 'package:selfprivacy/logic/models/user.dart';
import 'package:selfprivacy/utils/password_generator2.dart';
class HetznerApi extends ApiMap {
HetznerApi([String token]) {
HetznerApi([String? token]) {
if (token != null) {
loggedClient.options = BaseOptions(
headers: {'Authorization': 'Bearer $token'},
baseUrl: rootAddress,
baseUrl: rootAddress!,
);
}
}
@override
String rootAddress = 'https://api.hetzner.cloud/v1/servers';
String? rootAddress = 'https://api.hetzner.cloud/v1/servers';
Future<bool> isValid(String token) async {
var options = Options(
@ -29,7 +28,7 @@ class HetznerApi extends ApiMap {
},
);
Response response = await loggedClient.get(rootAddress, options: options);
Response response = await loggedClient.get(rootAddress!, options: options);
if (response.statusCode == HttpStatus.ok) {
return true;
@ -41,9 +40,9 @@ class HetznerApi extends ApiMap {
}
Future<HetznerServerDetails> createServer({
@required String cloudFlareKey,
@required User rootUser,
@required String domainName,
required String? cloudFlareKey,
required User rootUser,
required String? domainName,
}) async {
var dbPassword = getRandomString(40);
@ -52,7 +51,7 @@ class HetznerApi extends ApiMap {
);
Response response = await loggedClient.post(
rootAddress,
rootAddress!,
data: data,
);
@ -64,17 +63,17 @@ class HetznerApi extends ApiMap {
}
Future<void> deleteSelfprivacyServer({
@required String cloudFlareKey,
required String? cloudFlareKey,
}) async {
Response response = await loggedClient.get(rootAddress);
Response response = await loggedClient.get(rootAddress!);
List list = response.data['servers'];
var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server');
return await loggedClient.delete('$rootAddress/${server['id']}');
await loggedClient.delete('$rootAddress/${server['id']}');
}
Future<HetznerServerDetails> startServer({
HetznerServerDetails server,
required HetznerServerDetails server,
}) async {
await loggedClient.post('/${server.id}/actions/poweron');
@ -84,7 +83,7 @@ class HetznerApi extends ApiMap {
}
Future<HetznerServerDetails> restart({
HetznerServerDetails server,
required HetznerServerDetails server,
}) async {
await loggedClient.post('/${server.id}/actions/poweron');

View File

@ -5,7 +5,7 @@ import 'package:dio/dio.dart';
import 'api_map.dart';
class ServerApi extends ApiMap {
ServerApi(String domainName) {
ServerApi(String? domainName) {
loggedClient.options = BaseOptions(
baseUrl: 'https://api.$domainName',
);

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
@ -10,6 +9,7 @@ import 'package:selfprivacy/logic/models/server_details.dart';
import 'package:selfprivacy/logic/models/user.dart';
import 'app_config_repository.dart';
export 'package:provider/provider.dart';
part 'app_config_state.dart';
@ -64,23 +64,23 @@ class AppConfigCubit extends Cubit<AppConfigState> {
}
void startServerIfDnsIsOkay({
AppConfigState state,
AppConfigState? state,
bool isImmediate = false,
}) async {
state = state ?? this.state;
final work = () async {
emit(TimerState(dataState: state, isLoading: true));
emit(TimerState(dataState: state!, isLoading: true));
var ip4 = state.hetznerServer.ip4;
var domainName = state.cloudFlareDomain.domainName;
var ip4 = state.hetznerServer!.ip4;
var domainName = state.cloudFlareDomain!.domainName;
var isMatch = await repository.isDnsAddressesMatch(domainName, ip4);
if (isMatch) {
var server = await repository.startServer(
state.hetznerKey,
state.hetznerServer,
state.hetznerServer!,
);
repository.saveServerDetails(server);
emit(
@ -111,16 +111,16 @@ class AppConfigCubit extends Cubit<AppConfigState> {
}
void resetServerIfServerIsOkay({
AppConfigState state,
AppConfigState? state,
bool isImmediate = false,
}) async {
state = state ?? this.state;
var work = () async {
emit(TimerState(dataState: state, isLoading: true));
emit(TimerState(dataState: state!, isLoading: true));
var isServerWorking = await repository.isHttpServerWorking(
state.cloudFlareDomain.domainName,
state.cloudFlareDomain!.domainName,
);
if (isServerWorking) {
@ -133,8 +133,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
));
timer = Timer(pauseDuration, () async {
var hetznerServerDetails = await repository.restart(
state.hetznerKey,
state.hetznerServer,
state!.hetznerKey,
state.hetznerServer!,
);
emit(
state.copyWith(
@ -165,19 +165,19 @@ class AppConfigCubit extends Cubit<AppConfigState> {
}
}
Timer timer;
Timer? timer;
void finishCheckIfServerIsOkay({
AppConfigState state,
AppConfigState? state,
bool isImmediate = false,
}) async {
state = state ?? this.state;
var work = () async {
emit(TimerState(dataState: state, isLoading: true));
emit(TimerState(dataState: state!, isLoading: true));
var isServerWorking = await repository.isHttpServerWorking(
state.cloudFlareDomain.domainName,
state.cloudFlareDomain!.domainName,
);
if (isServerWorking) {
@ -238,12 +238,12 @@ class AppConfigCubit extends Cubit<AppConfigState> {
}
void createServerAndSetDnsRecords() async {
var _stateCopy = state;
AppConfigState _stateCopy = state;
var onSuccess = (serverDetails) async {
await repository.createDnsRecords(
state.cloudFlareKey,
serverDetails.ip4,
state.cloudFlareDomain,
state.cloudFlareDomain!,
);
emit(state.copyWith(
@ -259,8 +259,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
emit(state.copyWith(isLoading: true));
await repository.createServer(
state.hetznerKey,
state.rootUser,
state.cloudFlareDomain.domainName,
state.rootUser!,
state.cloudFlareDomain!.domainName,
state.cloudFlareKey,
onCancel: onCancel,
onSuccess: onSuccess,
@ -277,54 +277,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
}
void _closeTimer() {
if (timer != null && timer.isActive) {
timer.cancel();
if (timer != null && timer!.isActive) {
timer!.cancel();
}
}
}
// void checkDnsAndStartServer() async {
// var ip4 = state.hetznerServer.ip4;
// var domainName = state.cloudFlareDomain.domainName;
// var isMatch = await repository.isDnsAddressesMatch(domainName, ip4);
// if (isMatch) {
// var server = await repository.startServer(
// state.hetznerKey,
// state.hetznerServer,
// );
// repository.saveServerDetails(server);
// emit(
// state.copyWith(
// hasFinalChecked: true,
// isServerStarted: true,
// isLoading: false,
// hetznerServer: server,
// ),
// );
// } else {
// emit(state.copyWith(lastDnsCheckTime: DateTime.now()));
// }
// }
// void serverReset() async {
// var callBack = () async {
// var isServerWorking = await repository.isHttpServerWorking(
// state.cloudFlareDomain.domainName,
// );
// if (!isServerWorking) {
// var last = DateTime.now();
// // emit(state.copyWith(lastServerStatusCheckTime: last));
// return;
// }
// var hetznerServerDetails = await repository.restart(
// state.hetznerKey,
// state.hetznerServer,
// );
// emit(state.copyWith(hetznerServer: hetznerServerDetails));
// };
// _tryOrAddError(state, callBack);
// }

View File

@ -60,7 +60,7 @@ class AppConfigRepository {
}
Future<HetznerServerDetails> startServer(
String hetznerKey,
String? hetznerKey,
HetznerServerDetails hetznerServer,
) async {
var hetznerApi = HetznerApi(hetznerKey);
@ -75,7 +75,7 @@ class AppConfigRepository {
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>[
'$domainName',
@ -116,12 +116,12 @@ class AppConfigRepository {
}
Future<void> createServer(
String hetznerKey,
String? hetznerKey,
User rootUser,
String domainName,
String cloudFlareKey, {
void Function() onCancel,
Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
String? domainName,
String? cloudFlareKey, {
void Function()? onCancel,
required Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
}) async {
var hetznerApi = HetznerApi(hetznerKey);
@ -135,7 +135,7 @@ class AppConfigRepository {
hetznerApi.close();
onSuccess(serverDetails);
} 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>();
nav.showPopUpDialog(
BrandAlert(
@ -165,7 +165,7 @@ class AppConfigRepository {
text: 'Отменить',
onPressed: () {
hetznerApi.close();
onCancel();
onCancel!();
},
),
],
@ -176,8 +176,8 @@ class AppConfigRepository {
}
Future<void> createDnsRecords(
String cloudFlareKey,
String ip4,
String? cloudFlareKey,
String? ip4,
CloudFlareDomain cloudFlareDomain,
) async {
var cloudflareApi = CloudflareApi(cloudFlareKey);
@ -195,7 +195,7 @@ class AppConfigRepository {
cloudflareApi.close();
}
Future<bool> isHttpServerWorking(String domainName) async {
Future<bool> isHttpServerWorking(String? domainName) async {
var api = ServerApi(domainName);
var isHttpServerWorking = await api.isHttpServerWorking();
api.close();
@ -203,7 +203,7 @@ class AppConfigRepository {
}
Future<HetznerServerDetails> restart(
String hetznerKey,
String? hetznerKey,
HetznerServerDetails server,
) async {
var hetznerApi = HetznerApi(hetznerKey);

View File

@ -2,21 +2,21 @@ part of 'app_config_cubit.dart';
class AppConfigState extends Equatable {
const AppConfigState({
@required this.hetznerKey,
@required this.cloudFlareKey,
@required this.backblazeCredential,
@required this.cloudFlareDomain,
@required this.rootUser,
@required this.hetznerServer,
@required this.isServerStarted,
@required this.isServerReseted,
@required this.hasFinalChecked,
@required this.isLoading,
@required this.error,
required this.hetznerKey,
required this.cloudFlareKey,
required this.backblazeCredential,
required this.cloudFlareDomain,
required this.rootUser,
required this.hetznerServer,
required this.isServerStarted,
required this.isServerReseted,
required this.hasFinalChecked,
required this.isLoading,
required this.error,
});
@override
List<Object> get props => [
List<Object?> get props => [
hetznerKey,
cloudFlareKey,
backblazeCredential,
@ -30,31 +30,31 @@ class AppConfigState extends Equatable {
error,
];
final String hetznerKey;
final String cloudFlareKey;
final BackblazeCredential backblazeCredential;
final CloudFlareDomain cloudFlareDomain;
final User rootUser;
final HetznerServerDetails hetznerServer;
final bool isServerStarted;
final bool isServerReseted;
final bool hasFinalChecked;
final String? hetznerKey;
final String? cloudFlareKey;
final BackblazeCredential? backblazeCredential;
final CloudFlareDomain? cloudFlareDomain;
final User? rootUser;
final HetznerServerDetails? hetznerServer;
final bool? isServerStarted;
final bool? isServerReseted;
final bool? hasFinalChecked;
final bool isLoading;
final Exception error;
final bool? isLoading;
final Exception? error;
AppConfigState copyWith({
String hetznerKey,
String cloudFlareKey,
BackblazeCredential backblazeCredential,
CloudFlareDomain cloudFlareDomain,
User rootUser,
HetznerServerDetails hetznerServer,
bool isServerStarted,
bool isServerReseted,
bool hasFinalChecked,
bool isLoading,
Exception error,
String? hetznerKey,
String? cloudFlareKey,
BackblazeCredential? backblazeCredential,
CloudFlareDomain? cloudFlareDomain,
User? rootUser,
HetznerServerDetails? hetznerServer,
bool? isServerStarted,
bool? isServerReseted,
bool? hasFinalChecked,
bool? isLoading,
Exception? error,
}) =>
AppConfigState(
hetznerKey: hetznerKey ?? this.hetznerKey,
@ -77,10 +77,10 @@ class AppConfigState extends Equatable {
bool get isUserFilled => rootUser != null;
bool get isServerCreated => hetznerServer != null;
bool get isFullyInitilized => _fulfilementList.every((el) => el);
int get progress => _fulfilementList.where((el) => el).length;
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
int get progress => _fulfilementList.where((el) => el!).length;
List<bool> get _fulfilementList => [
List<bool?> get _fulfilementList => [
isHetznerFilled,
isCloudFlareFilled,
isBackblazeFilled,
@ -112,10 +112,10 @@ class InitialAppConfigState extends AppConfigState {
class TimerState extends AppConfigState {
TimerState({
@required this.dataState,
required this.dataState,
this.timerStart,
this.duration,
@required bool isLoading,
required bool isLoading,
}) : super(
hetznerKey: dataState.hetznerKey,
cloudFlareKey: dataState.cloudFlareKey,
@ -131,11 +131,11 @@ class TimerState extends AppConfigState {
);
final AppConfigState dataState;
final DateTime timerStart;
final Duration duration;
final DateTime? timerStart;
final Duration? duration;
@override
List<Object> get props => [
List<Object?> get props => [
dataState,
timerStart,
duration,

View File

@ -1,6 +1,5 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
export 'package:provider/provider.dart';
@ -9,8 +8,8 @@ part 'app_settings_state.dart';
class AppSettingsCubit extends Cubit<AppSettingsState> {
AppSettingsCubit({
@required bool isDarkModeOn,
@required bool isOnbordingShowing,
required bool isDarkModeOn,
required bool isOnbordingShowing,
}) : super(
AppSettingsState(
isDarkModeOn: isDarkModeOn,
@ -21,15 +20,15 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
Box box = Hive.box(BNames.appSettings);
void load() {
bool isDarkModeOn = box.get(BNames.isDarkModeOn);
bool isOnbordingShowing = box.get(BNames.isOnbordingShowing);
bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
bool? isOnbordingShowing = box.get(BNames.isOnbordingShowing);
emit(state.copyWith(
isDarkModeOn: isDarkModeOn,
isOnbordingShowing: isOnbordingShowing,
));
}
void updateDarkMode({@required bool isDarkModeOn}) {
void updateDarkMode({required bool isDarkModeOn}) {
box.put(BNames.isDarkModeOn, isDarkModeOn);
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
}

View File

@ -2,8 +2,8 @@ part of 'app_settings_cubit.dart';
class AppSettingsState extends Equatable {
const AppSettingsState({
@required this.isDarkModeOn,
@required this.isOnbordingShowing,
required this.isDarkModeOn,
required this.isOnbordingShowing,
});
final bool isDarkModeOn;

View File

@ -42,13 +42,14 @@ class BackblazeFormCubit extends FormCubit {
final AppConfigCubit initializingCubit;
FieldCubit<String> keyId;
FieldCubit<String> applicationKey;
// ignore: close_sinks
late final FieldCubit<String> keyId;
// ignore: close_sinks
late final FieldCubit<String> applicationKey;
@override
FutureOr<bool> asyncValidation() async {
bool isKeyValid;
late bool isKeyValid;
try {
String encodedApiKey = encodedBackblazeKey(
keyId.state.value,

View File

@ -30,11 +30,11 @@ class CloudFlareFormCubit extends FormCubit {
final AppConfigCubit initializingCubit;
FieldCubit<String> apiKey;
late final FieldCubit<String> apiKey;
@override
FutureOr<bool> asyncValidation() async {
bool isKeyValid;
late bool isKeyValid;
try {
isKeyValid = await apiClient.isValid(apiKey.state.value);

View File

@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
class DomainSetupCubit extends Cubit<DomainSetupState> {
DomainSetupCubit(this.initializingCubit) : super(Initial()) {
var token = (initializingCubit.state.cloudFlareKey);
var token = initializingCubit.state.cloudFlareKey;
assert(token != null, 'no cloudflare token');
@ -13,11 +13,11 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
}
AppConfigCubit initializingCubit;
CloudflareApi api;
late CloudflareApi api;
Future<void> load() async {
emit(Loading(LoadingTypes.loadingDomain));
var list = await api.domainList();
var list = await (api.domainList() as Future<List<String>>);
if (list.isEmpty) {
emit(Empty());
} else if (list.length == 1) {

View File

@ -30,11 +30,12 @@ class HetznerFormCubit extends FormCubit {
final AppConfigCubit initializingCubit;
FieldCubit<String> apiKey;
// ignore: close_sinks
late final FieldCubit<String> apiKey;
@override
FutureOr<bool> asyncValidation() async {
bool isKeyValid;
late bool isKeyValid;
try {
isKeyValid = await apiClient.isValid(apiKey.state.value);
} catch (e) {

View File

@ -46,9 +46,12 @@ class RootUserFormCubit extends FormCubit {
final AppConfigCubit initializingCubit;
FieldCubit<String> userName;
FieldCubit<String> password;
FieldCubit<bool> isVisible;
// ignore: close_sinks
late final FieldCubit<String> userName;
// ignore: close_sinks
late final FieldCubit<String> password;
// ignore: close_sinks
late final FieldCubit<bool> isVisible;
@override
Future<void> close() async {

View File

@ -7,8 +7,8 @@ import 'package:selfprivacy/utils/password_generator.dart';
class UserFormCubit extends FormCubit {
UserFormCubit({
this.usersCubit,
User user,
required this.usersCubit,
User? user,
}) {
var isEdit = user != null;
@ -16,7 +16,7 @@ class UserFormCubit extends FormCubit {
var passwordRegExp = RegExp(r"[\n\r\s]+");
login = FieldCubit(
initalValue: isEdit ? user.login : '',
initalValue: isEdit ? user!.login : '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
@ -25,7 +25,7 @@ class UserFormCubit extends FormCubit {
);
password = FieldCubit(
initalValue: isEdit ? user.password : genPass(),
initalValue: isEdit ? user!.password : genPass(),
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
@ -42,15 +42,16 @@ class UserFormCubit extends FormCubit {
login: login.state.value,
password: password.state.value,
);
usersCubit.add(user);
usersCubit.addUser(user);
}
FieldCubit<String> login;
FieldCubit<String> password;
// ignore: close_sinks
late FieldCubit<String> login;
late FieldCubit<String> password;
void genNewPassword() {
password.externalSetValue(genPass());
}
UsersCubit usersCubit;
late UsersCubit usersCubit;
}

View File

@ -5,7 +5,7 @@ class LegnthStringValidationWithLenghShowing extends ValidationModel<String> {
: super((n) => n.length != length, errorText);
@override
String check(String val) {
String? check(String val) {
var length = val.length;
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
return test(val) ? errorMassage : null;

View File

@ -1,28 +1,28 @@
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';
// 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';
// export 'package:provider/provider.dart';
// export 'package:selfprivacy/logic/models/state_types.dart';
part 'services_state.dart';
// part 'services_state.dart';
class ServicesCubit extends Cubit<ServicesState> {
ServicesCubit() : super(ServicesState(all));
// class ServicesCubit extends Cubit<ServicesState> {
// ServicesCubit() : super(ServicesState(all));
void connect(Service service) {
var newState = state.updateElement(service, StateType.stable);
emit(newState);
}
}
// 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();
// final all = ServiceTypes.values
// .map(
// (type) => Service(
// state: StateType.uninitialized,
// type: type,
// ),
// )
// .toList();

View File

@ -1,26 +1,26 @@
part of 'services_cubit.dart';
// part of 'services_cubit.dart';
@immutable
class ServicesState extends Equatable{
ServicesState(this.all);
// @immutable
// class ServicesState extends Equatable{
// ServicesState(this.all);
final List<Service> 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);
}
// 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 connected => all
// .where((service) => service.state != StateType.uninitialized)
// .toList();
List<Service> get uninitialized => 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;
}
// @override
// List<Object> get props => all;
// }

View File

@ -8,14 +8,14 @@ part 'users_state.dart';
class UsersCubit extends Cubit<UsersState> {
UsersCubit() : super(UsersState([]));
void add(User user) {
void addUser(User user) {
var users = [...state.users];
users.add(user);
emit(UsersState(users));
}
void remove(User user) {
void remove(User? user) {
var users = [...state.users];
users.remove(user);

View File

@ -3,10 +3,10 @@ import 'package:flutter/widgets.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get navigator => navigatorKey.currentState;
NavigatorState? get navigator => navigatorKey.currentState;
void showPopUpDialog(AlertDialog dialog) {
final context = navigatorKey.currentState.overlay.context;
final context = navigatorKey.currentState!.overlay!.context;
showDialog(
context: context,

View File

@ -9,10 +9,10 @@ class BackblazeCredential {
BackblazeCredential({this.keyId, this.applicationKey});
@HiveField(0)
final String keyId;
final String? keyId;
@HiveField(1)
final String applicationKey;
final String? applicationKey;
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
@ -22,7 +22,7 @@ class BackblazeCredential {
}
}
String encodedBackblazeKey(String keyId, String applicationKey) {
String encodedBackblazeKey(String? keyId, String? applicationKey) {
String _apiKey = '$keyId:$applicationKey';
String encodedApiKey = base64.encode(utf8.encode(_apiKey));
return encodedApiKey;

View File

@ -17,8 +17,8 @@ class BackblazeCredentialAdapter extends TypeAdapter<BackblazeCredential> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return BackblazeCredential(
keyId: fields[0] as String,
applicationKey: fields[1] as String,
keyId: fields[0] as String?,
applicationKey: fields[1] as String?,
);
}

View File

@ -7,10 +7,10 @@ class CloudFlareDomain {
CloudFlareDomain({this.domainName, this.zoneId});
@HiveField(0)
final String domainName;
final String? domainName;
@HiveField(1)
final String zoneId;
final String? zoneId;
@override
String toString() {

View File

@ -17,8 +17,8 @@ class CloudFlareDomainAdapter extends TypeAdapter<CloudFlareDomain> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return CloudFlareDomain(
domainName: fields[0] as String,
zoneId: fields[1] as String,
domainName: fields[0] as String?,
zoneId: fields[1] as String?,
);
}

View File

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
part 'dns_records.g.dart';
@ -6,17 +5,17 @@ part 'dns_records.g.dart';
@JsonSerializable(createToJson: true, createFactory: false)
class DnsRecords {
DnsRecords({
@required this.type,
@required this.name,
@required this.content,
required this.type,
required this.name,
required this.content,
this.ttl = 3600,
this.priority = 10,
this.proxied = false,
});
final String type;
final String name;
final String content;
final String? name;
final String? content;
final int ttl;
final int priority;
final bool proxied;

View File

@ -5,12 +5,12 @@ final formater = new DateFormat('hh:mm');
class Message {
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
final String text;
final String? text;
final DateTime time;
final MessageType type;
String get timeString => formater.format(time);
static Message warn({String text}) => Message(
static Message warn({String? text}) => Message(
text: text,
type: MessageType.warning,
);

View File

@ -10,7 +10,7 @@ enum ProviderType {
}
class ProviderModel extends Equatable {
const ProviderModel({this.state, this.type});
const ProviderModel({required this.state, required this.type});
final StateType state;
final ProviderType type;
@ -21,7 +21,7 @@ class ProviderModel extends Equatable {
);
@override
List<Object> get props => [state, type];
List<Object?> get props => [state, type];
IconData get icon {
switch (type) {
@ -31,10 +31,8 @@ class ProviderModel extends Equatable {
case ProviderType.domain:
return BrandIcons.globe;
break;
case ProviderType.backup:
return BrandIcons.save;
}
return null;
}
}

View File

@ -1,4 +1,3 @@
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart';
part 'server_details.g.dart';
@ -6,25 +5,25 @@ part 'server_details.g.dart';
@HiveType(typeId: 2)
class HetznerServerDetails {
HetznerServerDetails({
@required this.ip4,
@required this.id,
@required this.createTime,
required this.ip4,
required this.id,
required this.createTime,
this.startTime,
});
@HiveField(0)
final String ip4;
final String? ip4;
@HiveField(1)
final int id;
final int? id;
@HiveField(3)
final DateTime createTime;
final DateTime? createTime;
@HiveField(2)
final DateTime startTime;
final DateTime? startTime;
HetznerServerDetails copyWith({DateTime startTime}) {
HetznerServerDetails copyWith({DateTime? startTime}) {
return HetznerServerDetails(
startTime: startTime ?? this.startTime,
createTime: createTime,

View File

@ -17,10 +17,10 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return HetznerServerDetails(
ip4: fields[0] as String,
id: fields[1] as int,
createTime: fields[3] as DateTime,
startTime: fields[2] as DateTime,
ip4: fields[0] as String?,
id: fields[1] as int?,
createTime: fields[3] as DateTime?,
startTime: fields[2] as DateTime?,
);
}

View File

@ -1,12 +1,10 @@
import 'package:flutter/foundation.dart';
class ServerStatus {
final StatusTypes http;
final StatusTypes imap;
final StatusTypes smtp;
ServerStatus({
@required this.http,
required this.http,
this.imap = StatusTypes.nodata,
this.smtp = StatusTypes.nodata,
});
@ -20,7 +18,7 @@ class ServerStatus {
}
}
StatusTypes statusTypeFromNumber(int number) {
StatusTypes statusTypeFromNumber(int? number) {
if (number == 0) {
return StatusTypes.ok;
} else if (number == 1) {

View File

@ -1,25 +1,25 @@
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
// import 'package:equatable/equatable.dart';
// import 'package:selfprivacy/logic/models/state_types.dart';
enum ServiceTypes {
messanger,
mail,
passwordManager,
github,
cloud,
}
// enum ServiceTypes {
// messanger,
// mail,
// passwordManager,
// github,
// cloud,
// }
class Service extends Equatable {
const Service({this.state, this.type});
// class Service extends Equatable {
// const Service({required this.state, required this.type});
final StateType state;
final ServiceTypes type;
// final StateType state;
// final ServiceTypes type;
Service updateState(StateType newState) => Service(
state: newState,
type: type,
);
// Service updateState(StateType newState) => Service(
// state: newState,
// type: type,
// );
@override
List<Object> get props => [state, type];
}
// @override
// List<Object?> get props => [state, type];
// }

View File

@ -1,7 +1,6 @@
import 'dart:ui';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:selfprivacy/utils/color_utils.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/utils/crypto.dart';
@ -11,18 +10,18 @@ part 'user.g.dart';
@HiveType(typeId: 1)
class User extends Equatable {
User({
@required this.login,
@required this.password,
required this.login,
required this.password,
});
@HiveField(0)
final String login;
@HiveField(1)
final String password;
@override
List<Object> get props => [login, password];
List<Object?> get props => [login, password];
Color get color => stringToColor(login);

View File

@ -22,6 +22,7 @@ void main() async {
Wakelock.enable();
getItSetup();
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
Localization(
@ -35,7 +36,7 @@ void main() async {
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appSettings = context.watch<AppSettingsCubit>().state;
AppSettingsState appSettings = context.watch<AppSettingsCubit>().state;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
@ -50,12 +51,12 @@ class MyApp extends StatelessWidget {
home: appSettings.isOnbordingShowing
? OnboardingPage(nextPage: InitializingPage())
: RootPage(),
builder: (BuildContext context, Widget widget) {
builder: (BuildContext context, Widget? widget) {
Widget error = Text('...rendering error...');
if (widget is Scaffold || widget is Navigator)
error = Scaffold(body: Center(child: error));
ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error;
return widget;
return widget!;
},
),
);

View File

@ -3,14 +3,14 @@ import 'package:selfprivacy/config/brand_colors.dart';
class ActionButton extends StatelessWidget {
const ActionButton({
Key key,
Key? key,
this.text,
this.onPressed,
this.isRed = false,
}) : super(key: key);
final VoidCallback onPressed;
final String text;
final VoidCallback? onPressed;
final String? text;
final bool isRed;
@override
@ -19,12 +19,12 @@ class ActionButton extends StatelessWidget {
return TextButton(
child: Text(
text,
text!,
style: isRed ? TextStyle(color: BrandColors.red1) : null,
),
onPressed: () {
navigator.pop();
if (onPressed != null) onPressed();
if (onPressed != null) onPressed!();
},
);
}

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
class BrandAlert extends AlertDialog {
BrandAlert({
Key key,
String title,
String contentText,
List<Widget> acitons,
Key? key,
String? title,
String? contentText,
List<Widget>? acitons,
}) : super(
key: key,
title: title != null ? Text(title) : null,
content: title != null ? Text(contentText) : null,
content: title != null ? Text(contentText!) : null,
actions: acitons,
);
}

View File

@ -8,10 +8,10 @@ enum BrandButtonTypes { rised, text, iconText }
class BrandButton {
static rised({
Key key,
@required VoidCallback onPressed,
String title,
Widget child,
Key? key,
required VoidCallback? onPressed,
String? title,
Widget? child,
}) {
assert(title == null || child == null, 'required title or child');
assert(title != null || child != null, 'required title or child');
@ -24,9 +24,9 @@ class BrandButton {
}
static text({
Key key,
@required VoidCallback onPressed,
@required String title,
Key? key,
required VoidCallback onPressed,
required String title,
}) =>
_TextButton(
key: key,
@ -35,10 +35,10 @@ class BrandButton {
);
static iconText({
Key key,
@required VoidCallback onPressed,
@required String title,
@required Icon icon,
Key? key,
required VoidCallback onPressed,
required String title,
required Icon icon,
}) =>
_IconTextButton(
key: key,
@ -50,15 +50,15 @@ class BrandButton {
class _RisedButton extends StatelessWidget {
const _RisedButton({
Key key,
Key? key,
this.onPressed,
this.title,
this.child,
}) : super(key: key);
final VoidCallback onPressed;
final String title;
final Widget child;
final VoidCallback? onPressed;
final String? title;
final Widget? child;
@override
Widget build(BuildContext context) {
@ -88,13 +88,13 @@ class _RisedButton extends StatelessWidget {
class _TextButton extends StatelessWidget {
const _TextButton({
Key key,
Key? key,
this.onPressed,
this.title,
}) : super(key: key);
final VoidCallback onPressed;
final String title;
final VoidCallback? onPressed;
final String? title;
@override
Widget build(BuildContext context) {
@ -106,7 +106,7 @@ class _TextButton extends StatelessWidget {
alignment: Alignment.center,
padding: EdgeInsets.all(12),
child: Text(
title,
title!,
style: TextStyle(
color: BrandColors.blue,
fontSize: 16,
@ -120,12 +120,12 @@ class _TextButton extends StatelessWidget {
}
class _IconTextButton extends StatelessWidget {
const _IconTextButton({Key key, this.onPressed, this.title, this.icon})
const _IconTextButton({Key? key, this.onPressed, this.title, this.icon})
: super(key: key);
final VoidCallback onPressed;
final String title;
final Icon icon;
final VoidCallback? onPressed;
final String? title;
final Icon? icon;
@override
Widget build(BuildContext context) {

View File

@ -4,11 +4,11 @@ import 'package:selfprivacy/utils/extensions/elevation_extension.dart';
class BrandCard extends StatelessWidget {
const BrandCard({
Key key,
Key? key,
this.child,
}) : super(key: key);
final Widget child;
final Widget? child;
@override
Widget build(BuildContext context) {

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
class BrandDivider extends StatelessWidget {
const BrandDivider({Key key}) : super(key: key);
const BrandDivider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -4,8 +4,8 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class BrandHeader extends StatelessWidget {
const BrandHeader({
Key key,
@required this.title,
Key? key,
required this.title,
this.hasBackButton = false,
}) : super(key: key);

View File

@ -1,5 +1,5 @@
/// Flutter icons BrandIcons
/// Copyright (C) 2020 by original authors @ fluttericon.com, fontello.com
/// Copyright (C) 2021 by original authors @ fluttericon.com, fontello.com
/// This font was generated by FlutterIcon.com, which is derived from Fontello.
///
/// To use this font, place it in your fonts/ directory and include the
@ -19,7 +19,7 @@ class BrandIcons {
BrandIcons._();
static const _kFontFam = 'BrandIcons';
static const _kFontPkg = null;
static const String? _kFontPkg = null;
static const IconData connection =
IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
@ -39,14 +39,22 @@ class BrandIcons {
IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData check =
IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData webcam =
IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData refresh =
IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData git =
IconData(0xe80b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData social =
IconData(0xe80c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData settings =
IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData share =
IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData triangle =
IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData engineer =
IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData server =
IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData box =
@ -59,8 +67,16 @@ class BrandIcons {
IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData upload =
IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData github =
IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData arrow_left =
IconData(0xe818, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData shape =
IconData(0xe819, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData keyhole =
IconData(0xe81a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData terminal =
IconData(0xe81b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData fire =
IconData(0xe81c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData start =
IconData(0xe81d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:url_launcher/url_launcher.dart';
class BrandMarkdown extends StatefulWidget {
const BrandMarkdown({
Key? key,
required this.fileName,
}) : super(key: key);
final String fileName;
@override
_BrandMarkdownState createState() => _BrandMarkdownState();
}
class _BrandMarkdownState extends State<BrandMarkdown> {
String _mdContent = '';
@override
void initState() {
super.initState();
_loadMdFile();
}
void _loadMdFile() async {
String mdFromFile = await rootBundle
.loadString('assets/markdown/${widget.fileName}-${'locale'.tr()}.md');
setState(() {
_mdContent = mdFromFile;
});
}
@override
Widget build(BuildContext context) {
var isDark = Theme.of(context).brightness == Brightness.dark;
var markdown = MarkdownStyleSheet(
p: defaultTextStyle,
h1: headline1Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h2: headline2Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h3: headline3Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h4: headline4Style.copyWith(
color: isDark ? BrandColors.white : null,
),
);
return Markdown(
styleSheet: markdown,
onTapLink: (String text, String? href, String title) {
if (href != null) {
canLaunch(href).then((canLaunchURL) {
if (canLaunchURL) {
launch(href);
}
});
}
},
data: _mdContent,
);
}
}

View File

@ -4,11 +4,11 @@ var navigatorKey = GlobalKey<NavigatorState>();
class BrandModalSheet extends StatelessWidget {
const BrandModalSheet({
Key key,
Key? key,
this.child,
}) : super(key: key);
final Widget child;
final Widget? child;
@override
Widget build(BuildContext context) {
return DraggableScrollableSheet(

View File

@ -5,21 +5,19 @@ import 'package:url_launcher/url_launcher.dart';
class BrandSpanButton extends TextSpan {
BrandSpanButton({
@required String text,
@required VoidCallback onTap,
TextStyle style,
}) : assert(text != null),
assert(onTap != null),
super(
required String text,
required VoidCallback onTap,
TextStyle? style,
}) : super(
recognizer: TapGestureRecognizer()..onTap = onTap,
text: text,
style: (style ?? TextStyle()).copyWith(color: BrandColors.blue),
);
static link({
@required String text,
String urlString,
TextStyle style,
required String text,
String? urlString,
TextStyle? style,
}) =>
BrandSpanButton(
text: text,

View File

@ -1,37 +1,38 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:easy_localization/easy_localization.dart';
final _kBottomTabBarHeight = 51;
class BrandTabBar extends StatefulWidget {
BrandTabBar({Key key, this.controller}) : super(key: key);
BrandTabBar({Key? key, this.controller}) : super(key: key);
final TabController controller;
final TabController? controller;
@override
_BrandTabBarState createState() => _BrandTabBarState();
}
class _BrandTabBarState extends State<BrandTabBar> {
int currentIndex;
int? currentIndex;
@override
void initState() {
currentIndex = widget.controller.index;
widget.controller.addListener(_listener);
currentIndex = widget.controller!.index;
widget.controller!.addListener(_listener);
super.initState();
}
_listener() {
if (currentIndex != widget.controller.index) {
if (currentIndex != widget.controller!.index) {
setState(() {
currentIndex = widget.controller.index;
currentIndex = widget.controller!.index;
});
}
}
@override
void dispose() {
widget.controller ?? widget.controller.removeListener(_listener);
widget.controller ?? widget.controller!.removeListener(_listener);
super.dispose();
}
@ -50,10 +51,10 @@ class _BrandTabBarState extends State<BrandTabBar> {
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_getIconButton('Провайдеры', BrandIcons.server, 0),
_getIconButton('Сервисы', BrandIcons.box, 1),
_getIconButton('Пользователи', BrandIcons.users, 2),
_getIconButton('Еще', BrandIcons.menu, 3),
_getIconButton('basis.providers'.tr(), BrandIcons.server, 0),
_getIconButton('basis.services'.tr(), BrandIcons.box, 1),
_getIconButton('basis.users'.tr(), BrandIcons.users, 2),
_getIconButton('basis.more'.tr(), BrandIcons.menu, 3),
],
),
),
@ -68,7 +69,7 @@ class _BrandTabBarState extends State<BrandTabBar> {
var isActive = currentIndex == index;
var color = isActive ? acitivColor : BrandColors.inactive;
return InkWell(
onTap: () => widget.controller.animateTo(index),
onTap: () => widget.controller!.animateTo(index),
child: Padding(
padding: EdgeInsets.all(6),
child: ConstrainedBox(

View File

@ -17,26 +17,26 @@ enum TextType {
class BrandText extends StatelessWidget {
const BrandText(
this.text, {
Key key,
Key? key,
this.style,
@required this.type,
required this.type,
this.overflow,
this.softWrap,
this.textAlign,
}) : super(key: key);
final String text;
final TextStyle style;
final String? text;
final TextStyle? style;
final TextType type;
final TextOverflow overflow;
final bool softWrap;
final TextAlign textAlign;
final TextOverflow? overflow;
final bool? softWrap;
final TextAlign? textAlign;
factory BrandText.h1(
String text, {
TextStyle style,
TextOverflow overflow,
bool softWrap,
String? text, {
TextStyle? style,
TextOverflow? overflow,
bool? softWrap,
}) =>
BrandText(
text,
@ -44,18 +44,18 @@ class BrandText extends StatelessWidget {
style: style,
);
factory BrandText.onboardingTitle(String text, {TextStyle style}) =>
factory BrandText.onboardingTitle(String text, {TextStyle? style}) =>
BrandText(
text,
type: TextType.onboardingTitle,
style: style,
);
factory BrandText.h2(String text, {TextStyle style}) => BrandText(
factory BrandText.h2(String? text, {TextStyle? style}) => BrandText(
text,
type: TextType.h2,
style: style,
);
factory BrandText.h3(String text, {TextStyle style, TextAlign textAlign}) =>
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
BrandText(
text,
type: TextType.h3,
@ -63,35 +63,35 @@ class BrandText extends StatelessWidget {
textAlign: textAlign,
overflow: TextOverflow.ellipsis,
);
factory BrandText.h4(String text, {TextStyle style}) => BrandText(
factory BrandText.h4(String? text, {TextStyle? style}) => BrandText(
text,
type: TextType.h4,
style: style,
);
factory BrandText.body1(String text, {TextStyle style}) => BrandText(
factory BrandText.body1(String? text, {TextStyle? style}) => BrandText(
text,
type: TextType.body1,
style: style,
);
factory BrandText.body2(String text, {TextStyle style}) => BrandText(
factory BrandText.body2(String? text, {TextStyle? style}) => BrandText(
text,
type: TextType.body2,
style: style,
);
factory BrandText.medium(String text,
{TextStyle style, TextAlign textAlign}) =>
factory BrandText.medium(String? text,
{TextStyle? style, TextAlign? textAlign}) =>
BrandText(
text,
type: TextType.medium,
style: style,
textAlign: textAlign,
);
factory BrandText.small(String text, {TextStyle style}) => BrandText(
factory BrandText.small(String text, {TextStyle? style}) => BrandText(
text,
type: TextType.small,
style: style,
);
factory BrandText.buttonTitleText(String text, {TextStyle style}) =>
factory BrandText.buttonTitleText(String? text, {TextStyle? style}) =>
BrandText(
text,
type: TextType.buttonTitleText,
@ -153,7 +153,7 @@ class BrandText extends StatelessWidget {
style = style.merge(this.style);
}
return Text(
text,
text!,
style: style,
overflow: overflow,
softWrap: softWrap,

View File

@ -6,21 +6,21 @@ import 'package:selfprivacy/utils/named_font_weight.dart';
class BrandTimer extends StatefulWidget {
const BrandTimer({
Key key,
@required this.startDateTime,
@required this.duration,
Key? key,
required this.startDateTime,
required this.duration,
}) : super(key: key);
final DateTime startDateTime;
final Duration duration;
final DateTime? startDateTime;
final Duration? duration;
@override
_BrandTimerState createState() => _BrandTimerState();
}
class _BrandTimerState extends State<BrandTimer> {
String _timeString;
Timer timer;
String? _timeString;
late Timer timer;
@override
void initState() {
@ -31,8 +31,8 @@ class _BrandTimerState extends State<BrandTimer> {
_timerStart() {
_timeString = diffenceFromStart;
timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
var timePassed = DateTime.now().difference(widget.startDateTime);
if (timePassed > widget.duration) {
var timePassed = DateTime.now().difference(widget.startDateTime!);
if (timePassed > widget.duration!) {
t.cancel();
} else {
_getTime();
@ -66,12 +66,12 @@ class _BrandTimerState extends State<BrandTimer> {
}
String get diffenceFromStart =>
_durationToString(DateTime.now().difference(widget.startDateTime));
_durationToString(DateTime.now().difference(widget.startDateTime!));
String _durationToString(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String twoDigitSeconds =
twoDigits(widget.duration.inSeconds - duration.inSeconds.remainder(60));
twoDigits(widget.duration!.inSeconds - duration.inSeconds.remainder(60));
return "$twoDigitSeconds cек";
}

View File

@ -3,9 +3,9 @@ import 'package:selfprivacy/config/brand_colors.dart';
class DotsIndicator extends StatelessWidget {
const DotsIndicator({
Key key,
@required this.activeIndex,
@required this.count,
Key? key,
required this.activeIndex,
required this.count,
}) : super(key: key);
final int activeIndex;

View File

@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class BrandError extends StatelessWidget {
const BrandError({Key key, this.error, this.stackTrace}) : super(key: key);
const BrandError({Key? key, this.error, this.stackTrace}) : super(key: key);
final Object error;
final StackTrace stackTrace;
final Object? error;
final StackTrace? stackTrace;
@override
Widget build(BuildContext context) {

View File

@ -3,14 +3,14 @@ import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
class IconStatusMask extends StatelessWidget {
IconStatusMask({this.child, this.status});
IconStatusMask({required this.child, required this.status});
final Icon child;
final StateType status;
@override
Widget build(BuildContext context) {
List<Color> colors;
late List<Color> colors;
switch (status) {
case StateType.uninitialized:
colors = BrandColors.uninitializedGradientColors;

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:selfprivacy/ui/pages/initializing/initializing.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:easy_localization/easy_localization.dart';
class NotReadyCard extends StatelessWidget {
const NotReadyCard({Key key}) : super(key: key);
const NotReadyCard({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -16,29 +18,34 @@ class NotReadyCard extends StatelessWidget {
text: TextSpan(
children: [
TextSpan(
text: 'Завершите настройку приложения используя ',
text: 'not_ready_card.1'.tr(),
style: TextStyle(color: BrandColors.white),
),
WidgetSpan(
child: GestureDetector(
child: Text(
'Мастер подключения',
style: TextStyle(
child: Padding(
padding: const EdgeInsets.only(bottom: 0.5),
child: GestureDetector(
onTap: () => Navigator.of(context).push(
materialRoute(
InitializingPage(),
),
),
child: Text(
'not_ready_card.2'.tr(),
style: body1Style.copyWith(
color: Theme.of(context).brightness == Brightness.dark
? Colors.blueAccent
? Colors.black
: BrandColors.white,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline),
),
onTap: () => Navigator.of(context).push(
materialRoute(
InitializingPage(),
decoration: TextDecoration.underline,
// height: 1.1,
),
),
),
),
),
TextSpan(
text: ' для продолжения работы',
text: 'not_ready_card.3'.tr(),
style: TextStyle(color: BrandColors.white),
),
],

View File

@ -7,9 +7,9 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class ProgressBar extends StatefulWidget {
ProgressBar({
Key key,
@required this.steps,
@required this.activeIndex,
Key? key,
required this.steps,
required this.activeIndex,
}) : super(key: key);
final int activeIndex;
@ -102,14 +102,14 @@ class _ProgressBarState extends State<ProgressBar> {
}
Expanded _stepTitle({
int index,
TextStyle style,
String step,
required int index,
TextStyle? style,
String? step,
}) {
var isActive = 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(
flex: 2,
child: RichText(

View File

@ -3,10 +3,10 @@ import 'package:selfprivacy/config/brand_colors.dart';
class SwitcherBlock extends StatelessWidget {
const SwitcherBlock({
Key key,
@required this.child,
@required this.isActive,
@required this.onChange,
Key? key,
required this.child,
required this.isActive,
required this.onChange,
}) : super(key: key);
final Widget child;

View File

@ -1,9 +1,7 @@
import 'package:cubit_form/cubit_form.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/domain_cloudflare.dart';
@ -13,13 +11,14 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:easy_localization/easy_localization.dart';
class InitializingPage extends StatelessWidget {
@override
@ -60,9 +59,9 @@ class InitializingPage extends StatelessWidget {
'Domain',
'User',
'Server',
'Check1',
'Check2',
'Check3'
'',
'',
''
],
activeIndex: cubit.state.progress,
),
@ -76,12 +75,13 @@ class InitializingPage extends StatelessWidget {
),
),
BrandButton.text(
title:
cubit.state.isFullyInitilized ? 'Close' : 'Настрою потом',
title: cubit.state.isFullyInitilized
? 'basis.close'.tr()
: 'Настрою потом',
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(RootPage()),
(predicate) => predicate == null,
(predicate) => false,
);
}),
SizedBox(height: 30),
@ -96,7 +96,7 @@ class InitializingPage extends StatelessWidget {
return BlocProvider(
create: (context) => HetznerFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubit = context.watch<HetznerFormCubit>();
var formCubitState = context.watch<HetznerFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -105,13 +105,12 @@ class InitializingPage extends StatelessWidget {
width: 150,
),
SizedBox(height: 10),
BrandText.h2('Подключите сервер Hetzner'),
BrandText.h2('initializing.1'.tr()),
SizedBox(height: 10),
BrandText.body2(
'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
BrandText.body2('initializing.2'.tr()),
Spacer(),
CubitFormTextField(
formFieldCubit: formCubit.apiKey,
formFieldCubit: context.read<HetznerFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
@ -120,14 +119,15 @@ class InitializingPage extends StatelessWidget {
),
Spacer(),
BrandButton.rised(
onPressed:
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
title: 'Подключить',
onPressed: formCubitState.isSubmitting
? null
: () => context.read<HetznerFormCubit>().trySubmit(),
title: 'basis.connect'.tr(),
),
SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
title: 'initializing.how'.tr(),
),
],
);
@ -150,7 +150,7 @@ class InitializingPage extends StatelessWidget {
return BlocProvider(
create: (context) => CloudFlareFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubit = context.watch<CloudFlareFormCubit>();
var formCubitState = context.watch<CloudFlareFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -160,28 +160,29 @@ class InitializingPage extends StatelessWidget {
width: 150,
),
SizedBox(height: 10),
BrandText.h2('Подключите CloudFlare'),
BrandText.h2('initializing.3'.tr()),
SizedBox(height: 10),
BrandText.body2('Для управления DNS вашего домена'),
BrandText.body2('initializing.4'.tr()),
Spacer(),
CubitFormTextField(
formFieldCubit: formCubit.apiKey,
formFieldCubit: context.read<CloudFlareFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'CloudFlare API Token',
hintText: 'initializing.5'.tr(),
),
),
Spacer(),
BrandButton.rised(
onPressed:
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
title: 'Подключить',
onPressed: formCubitState.isSubmitting
? null
: () => context.read<CloudFlareFormCubit>().trySubmit(),
title: 'basis.connect'.tr(),
),
SizedBox(height: 10),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
title: 'initializing.how'.tr(),
),
],
);
@ -193,7 +194,7 @@ class InitializingPage extends StatelessWidget {
return BlocProvider(
create: (context) => BackblazeFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubit = context.watch<BackblazeFormCubit>();
var formCubitState = context.watch<BackblazeFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -202,11 +203,11 @@ class InitializingPage extends StatelessWidget {
height: 50,
),
SizedBox(height: 10),
BrandText.h2('Подключите облачное хранилище Backblaze'),
BrandText.h2('initializing.6'.tr()),
SizedBox(height: 10),
Spacer(),
CubitFormTextField(
formFieldCubit: formCubit.keyId,
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
@ -215,7 +216,7 @@ class InitializingPage extends StatelessWidget {
),
Spacer(),
CubitFormTextField(
formFieldCubit: formCubit.applicationKey,
formFieldCubit: context.read<BackblazeFormCubit>().applicationKey,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
@ -224,14 +225,15 @@ class InitializingPage extends StatelessWidget {
),
Spacer(),
BrandButton.rised(
onPressed:
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
title: 'Подключить',
onPressed: formCubitState.isSubmitting
? null
: () => context.read<BackblazeFormCubit>().trySubmit(),
title: 'basis.connect'.tr(),
),
SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
title: 'initializing.how'.tr(),
),
],
);
@ -243,8 +245,7 @@ class InitializingPage extends StatelessWidget {
return BlocProvider(
create: (context) => DomainSetupCubit(initializingCubit)..load(),
child: Builder(builder: (context) {
var domainSetup = context.watch<DomainSetupCubit>();
var state = domainSetup.state;
DomainSetupState state = context.watch<DomainSetupCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -253,19 +254,18 @@ class InitializingPage extends StatelessWidget {
width: 150,
),
SizedBox(height: 30),
BrandText.h2('Домен'),
BrandText.h2('basis.domain'.tr()),
SizedBox(height: 10),
if (state is Empty)
BrandText.body2('На данный момент подлюченных доменов нет'),
if (state is Empty) BrandText.body2('initializing.7'.tr()),
if (state is Loading)
BrandText.body2(
state.type == LoadingTypes.loadingDomain
? 'Загружаем список доменов'
: 'Сохранение..',
? 'initializing.8'.tr()
: 'basis.saving'.tr(),
),
if (state is MoreThenOne)
BrandText.body2(
'Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены',
'initializing.9'.tr(),
),
if (state is Loaded) ...[
SizedBox(height: 10),
@ -283,7 +283,7 @@ class InitializingPage extends StatelessWidget {
Container(
width: 50,
child: BrandButton.rised(
onPressed: () => domainSetup.load(),
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
@ -302,7 +302,7 @@ class InitializingPage extends StatelessWidget {
if (state is Empty) ...[
SizedBox(height: 30),
BrandButton.rised(
onPressed: () => domainSetup.load(),
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -319,8 +319,8 @@ class InitializingPage extends StatelessWidget {
if (state is Loaded) ...[
SizedBox(height: 30),
BrandButton.rised(
onPressed: () => domainSetup.saveDomain(),
title: 'Сохранить домен',
onPressed: () => context.read<DomainSetupCubit>().saveDomain(),
title: 'initializing.10'.tr(),
),
],
SizedBox(height: 10),
@ -328,7 +328,7 @@ class InitializingPage extends StatelessWidget {
SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
title: 'initializing.how'.tr(),
),
],
);
@ -340,7 +340,7 @@ class InitializingPage extends StatelessWidget {
return BlocProvider(
create: (context) => RootUserFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubit = context.watch<RootUserFormCubit>();
var formCubitState = context.watch<RootUserFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -348,30 +348,33 @@ class InitializingPage extends StatelessWidget {
Spacer(),
SizedBox(height: 10),
CubitFormTextField(
formFieldCubit: formCubit.userName,
formFieldCubit: context.read<RootUserFormCubit>().userName,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'Никнейм',
hintText: 'basis.nickname'.tr(),
),
),
SizedBox(height: 10),
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
cubit: formCubit.isVisible,
bloc: context.read<RootUserFormCubit>().isVisible,
builder: (context, state) {
var isVisible = state.value;
return CubitFormTextField(
obscureText: !isVisible,
formFieldCubit: formCubit.password,
formFieldCubit: context.read<RootUserFormCubit>().password,
textAlign: TextAlign.center,
scrollPadding: EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'Пароль',
hintText: 'basis.password'.tr(),
suffixIcon: IconButton(
icon: Icon(
isVisible ? Icons.visibility : Icons.visibility_off,
),
onPressed: () => formCubit.isVisible.setValue(!isVisible),
onPressed: () => context
.read<RootUserFormCubit>()
.isVisible
.setValue(!isVisible),
),
suffixIconConstraints: BoxConstraints(minWidth: 60),
prefixIconConstraints: BoxConstraints(maxWidth: 85),
@ -382,14 +385,15 @@ class InitializingPage extends StatelessWidget {
),
Spacer(),
BrandButton.rised(
onPressed:
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
title: 'Подключить',
onPressed: formCubitState.isSubmitting
? null
: () => context.read<RootUserFormCubit>().trySubmit(),
title: 'basis.connect'.tr(),
),
SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
title: 'initializing.how'.tr(),
),
],
);
@ -404,19 +408,19 @@ class InitializingPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Spacer(flex: 2),
BrandText.h2('Создать сервер'),
BrandText.h2('initializing.how'.tr()),
SizedBox(height: 10),
BrandText.body2('Создать сервер'),
BrandText.body2('initializing.11'.tr()),
Spacer(),
BrandButton.rised(
onPressed:
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
title: isLoading ? 'loading' : 'Создать сервер',
isLoading! ? null : appConfigCubit.createServerAndSetDnsRecords,
title: isLoading ? 'loading' : 'initializing.11'.tr(),
),
Spacer(flex: 2),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Что это значит?',
title: 'initializing.what'.tr(),
),
],
);
@ -427,13 +431,13 @@ class InitializingPage extends StatelessWidget {
assert(appConfigCubit.state is TimerState, 'wronge state');
var state = appConfigCubit.state as TimerState;
String text;
if (state.isServerReseted) {
text = 'Сервер презагружен, ждем последнюю проверку';
} else if (state.isServerStarted) {
text = 'Cервер запушен, сейчас он будет проверен и перезагружен';
String? text;
if (state.isServerReseted!) {
text = 'initializing.13'.tr();
} else if (state.isServerStarted!) {
text = 'initializing.14'.tr();
} else if (state.isServerCreated) {
text = 'Cервер создан, идет проверка ДНС адресов и запуск сервера';
text = 'initializing.15'.tr();
}
return Builder(builder: (context) {
return Column(
@ -443,23 +447,23 @@ class InitializingPage extends StatelessWidget {
SizedBox(height: 10),
BrandText.body2(text),
SizedBox(height: 10),
if (!state.isLoading)
if (!state.isLoading!)
Row(
children: [
BrandText.body2('До следующей проверки: '),
BrandText.body2('initializing.16'.tr()),
BrandTimer(
startDateTime: state.timerStart,
duration: state.duration,
)
],
),
if (state.isLoading) BrandText.body2('Проверка'),
if (state.isLoading!) BrandText.body2('initializing.17'.tr()),
Spacer(
flex: 2,
),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Что это значит?',
title: 'initializing.what'.tr(),
),
],
);
@ -477,58 +481,17 @@ class InitializingPage extends StatelessWidget {
class _HowHetzner extends StatelessWidget {
const _HowHetzner({
Key key,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
var isDark = Theme.of(context).brightness == Brightness.dark;
return BrandModalSheet(
child: Padding(
padding: brandPagePadding2,
child: Column(
children: [
SizedBox(height: 40),
BrandText.h2('Как получить Hetzner API Token'),
SizedBox(height: 20),
RichText(
text: TextSpan(
children: [
TextSpan(
text: '1 Переходим по ссылке ',
style: body1Style.copyWith(
color: isDark ? BrandColors.white : BrandColors.black,
),
),
BrandSpanButton.link(
text: 'hetzner.com',
urlString: 'https://hetzner.com',
),
TextSpan(
text: '''
2 Заходим в созданный нами проект. Если такового - нет, значит создаём.
3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний Security (с иконкой ключика).
4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.
5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
''',
style: body1Style.copyWith(
color: isDark ? BrandColors.white : BrandColors.black,
),
),
],
),
),
],
),
),
padding: brandPagePadding1,
child: BrandMarkdown(
fileName: 'how_hetzner',
)),
);
}
}

View File

@ -1,48 +1,24 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
class AboutPage extends StatelessWidget {
const AboutPage({Key key}) : super(key: key);
const AboutPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
child: BrandHeader(title: 'О проекте', hasBackButton: true),
child: BrandHeader(
title: 'more.about_project'.tr(), hasBackButton: true),
preferredSize: Size.fromHeight(52),
),
body: ListView(
padding: brandPagePadding2,
children: [
BrandDivider(),
SizedBox(height: 20),
BrandText.h3('О проекте'),
SizedBox(height: 10),
BrandText.body1(
'Всё больше организаций хотят владеть нашими данными'),
SizedBox(height: 10),
BrandText.body1(
'А мы сами хотим распоряжаться своими данными на своем сервере.'),
SizedBox(height: 20),
BrandDivider(),
SizedBox(height: 10),
BrandText.h3('Миссия проекта'),
SizedBox(height: 10),
BrandText.body1(
'Цифровая независимость и приватность доступная каждому'),
SizedBox(height: 20),
BrandDivider(),
SizedBox(height: 10),
BrandText.h3('Цель'),
SizedBox(height: 10),
BrandText.body1(
'Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких'),
SizedBox(height: 10),
],
body: Container(
child: BrandMarkdown(
fileName: 'about',
),
),
),
);

View File

@ -9,9 +9,10 @@ import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
import 'package:easy_localization/easy_localization.dart';
class AppSettingsPage extends StatefulWidget {
const AppSettingsPage({Key key}) : super(key: key);
const AppSettingsPage({Key? key}) : super(key: key);
@override
_AppSettingsPageState createState() => _AppSettingsPageState();
@ -20,16 +21,14 @@ class AppSettingsPage extends StatefulWidget {
class _AppSettingsPageState extends State<AppSettingsPage> {
@override
Widget build(BuildContext context) {
var appSettings = context.watch<AppSettingsCubit>();
var isDarkModeOn = appSettings.state.isDarkModeOn;
var isDarkModeOn = context.watch<AppSettingsCubit>().state.isDarkModeOn;
return SafeArea(
child: Builder(builder: (context) {
return Scaffold(
appBar: PreferredSize(
child:
BrandHeader(title: 'Настройки приложения', hasBackButton: true),
BrandHeader(title: 'more.settings'.tr(), hasBackButton: true),
preferredSize: Size.fromHeight(52),
),
body: ListView(
@ -57,8 +56,9 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
activeColor: BrandColors.green1,
activeTrackColor: BrandColors.green2,
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (value) => appSettings.updateDarkMode(
isDarkModeOn: !isDarkModeOn),
onChanged: (value) => context
.read<AppSettingsCubit>()
.updateDarkMode(isDarkModeOn: !isDarkModeOn),
),
],
),
@ -80,8 +80,10 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
),
),
SizedBox(width: 5),
RaisedButton(
color: BrandColors.red1,
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: BrandColors.red1,
),
child: Text(
'Reset',
style: TextStyle(
@ -92,24 +94,26 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
onPressed: () {
showDialog(
context: context,
child: BrandAlert(
title: 'Вы уверенны',
contentText: 'Сбросить все ключи?',
acitons: [
ActionButton(
text: 'Да, сбросить',
isRed: true,
onPressed: () {
context
.read<AppConfigCubit>()
.clearAppConfig();
Navigator.of(context).pop();
}),
ActionButton(
text: 'Отмена',
),
],
),
builder: (_) {
return BrandAlert(
title: 'Вы уверенны',
contentText: 'Сбросить все ключи?',
acitons: [
ActionButton(
text: 'Да, сбросить',
isRed: true,
onPressed: () {
context
.read<AppConfigCubit>()
.clearAppConfig();
Navigator.of(context).pop();
}),
ActionButton(
text: 'Отмена',
),
],
);
},
);
},
)
@ -126,9 +130,9 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
class _TextColumn extends StatelessWidget {
const _TextColumn({
Key key,
@required this.title,
@required this.value,
Key? key,
required this.title,
required this.value,
this.hasWarning = false,
}) : super(key: key);

View File

@ -9,7 +9,7 @@ import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
class Console extends StatefulWidget {
const Console({Key key}) : super(key: key);
const Console({Key? key}) : super(key: key);
@override
_ConsoleState createState() => _ConsoleState();

Some files were not shown because too many files have changed in this diff Show More