Merge pull request 'translations' (#3) from translations into master
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/3
2
.gitignore
vendored
|
@ -18,7 +18,7 @@
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
# 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
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
# is commented out by default.
|
# is commented out by default.
|
||||||
#.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Flutter/Dart/Pub related
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
|
|
13
.vscode/launch.json
vendored
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
BIN
assets/fonts/Inter-Bold.ttf
Normal file
BIN
assets/fonts/Inter-ExtraBold.ttf
Normal file
BIN
assets/fonts/Inter-Medium.ttf
Normal file
BIN
assets/fonts/Inter-Regular.ttf
Normal file
BIN
assets/fonts/Inter-SemiBold.ttf
Normal file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 149 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 141 KiB |
12
assets/markdown/about-en.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
### О проекте
|
||||||
|
|
||||||
|
Всё больше организаций хотят владеть нашими данными
|
||||||
|
А мы сами хотим распоряжаться своими **данными** на своем сервере.
|
||||||
|
|
||||||
|
### Миссия проекта
|
||||||
|
|
||||||
|
Цифровая независимость и приватность доступная каждому
|
||||||
|
|
||||||
|
### Цель
|
||||||
|
|
||||||
|
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких
|
12
assets/markdown/about-ru.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
### О проекте
|
||||||
|
|
||||||
|
Всё больше организаций хотят владеть нашими данными
|
||||||
|
А мы сами хотим распоряжаться своими **данными** на своем сервере.
|
||||||
|
|
||||||
|
### Миссия проекта
|
||||||
|
|
||||||
|
Цифровая независимость и приватность доступная каждому
|
||||||
|
|
||||||
|
### Цель
|
||||||
|
|
||||||
|
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких
|
7
assets/markdown/how_hetzner-en.md
Normal 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, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
|
7
assets/markdown/how_hetzner-ru.md
Normal 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, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
|
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет."
|
||||||
|
}
|
||||||
}
|
}
|
16
fastlane/metadata/android/en-US/full_description.txt
Normal 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
|
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 195 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
Normal file
After Width: | Height: | Size: 103 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Self-hosted services without pain
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
SelfPrivacy
|
1
fastlane/metadata/android/en-US/video.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
https://fdroid.selfprivacy.org/SFPresentation.mp4
|
|
@ -39,13 +39,13 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/wakelock/ios"
|
:path: ".symlinks/plugins/wakelock/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||||
wakelock: bfc7955c418d0db797614075aabbc58a39ab5107
|
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||||
|
|
||||||
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
|
@ -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_settings/app_settings_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/providers/providers_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';
|
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||||
|
|
||||||
class BlocAndProviderConfig extends StatelessWidget {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -30,7 +29,6 @@ class BlocAndProviderConfig extends StatelessWidget {
|
||||||
lazy: false,
|
lazy: false,
|
||||||
create: (_) => AppConfigCubit()..load(),
|
create: (_) => AppConfigCubit()..load(),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (_) => ServicesCubit()),
|
|
||||||
BlocProvider(create: (_) => ProvidersCubit()),
|
BlocProvider(create: (_) => ProvidersCubit()),
|
||||||
BlocProvider(create: (_) => UsersCubit()),
|
BlocProvider(create: (_) => UsersCubit()),
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,14 +2,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/ui/components/error/error.dart';
|
import 'package:selfprivacy/ui/components/error/error.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
|
||||||
import 'get_it_config.dart';
|
import './get_it_config.dart';
|
||||||
|
|
||||||
class SimpleBlocObserver extends BlocObserver {
|
class SimpleBlocObserver extends BlocObserver {
|
||||||
SimpleBlocObserver();
|
SimpleBlocObserver();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(Cubit cubit, Object error, StackTrace stackTrace) {
|
void onError(Bloc cubit, Object error, StackTrace stackTrace) {
|
||||||
final navigator = getIt.get<NavigationService>().navigator;
|
final navigator = getIt.get<NavigationService>().navigator!;
|
||||||
|
|
||||||
navigator.push(
|
navigator.push(
|
||||||
materialRoute(
|
materialRoute(
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:selfprivacy/config/text_themes.dart';
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
|
|
||||||
import 'brand_colors.dart';
|
import 'brand_colors.dart';
|
||||||
|
|
||||||
final ligtTheme = ThemeData(
|
final ligtTheme = ThemeData(
|
||||||
primaryColor: BrandColors.primary,
|
primaryColor: BrandColors.primary,
|
||||||
|
fontFamily: 'Inter',
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
|
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
@ -33,21 +33,17 @@ final ligtTheme = ThemeData(
|
||||||
color: BrandColors.red1,
|
color: BrandColors.red1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorStyle: GoogleFonts.inter(
|
errorStyle: TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: 12,
|
||||||
fontSize: 12,
|
color: BrandColors.red1,
|
||||||
color: BrandColors.red1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
textTheme: GoogleFonts.interTextTheme(
|
textTheme: TextTheme(
|
||||||
TextTheme(
|
headline1: headline1Style,
|
||||||
headline1: headline1Style,
|
headline2: headline2Style,
|
||||||
headline2: headline2Style,
|
caption: headline4Style,
|
||||||
caption: headline4Style,
|
bodyText1: body1Style,
|
||||||
bodyText1: body1Style,
|
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
|
||||||
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -57,14 +53,12 @@ var darkTheme = ligtTheme.copyWith(
|
||||||
iconTheme: IconThemeData(color: BrandColors.gray3),
|
iconTheme: IconThemeData(color: BrandColors.gray3),
|
||||||
cardColor: BrandColors.gray1,
|
cardColor: BrandColors.gray1,
|
||||||
dialogBackgroundColor: Color(0xFF202120),
|
dialogBackgroundColor: Color(0xFF202120),
|
||||||
textTheme: GoogleFonts.interTextTheme(
|
textTheme: TextTheme(
|
||||||
TextTheme(
|
headline1: headline1Style.copyWith(color: BrandColors.white),
|
||||||
headline1: headline1Style.copyWith(color: BrandColors.white),
|
headline2: headline2Style.copyWith(color: BrandColors.white),
|
||||||
headline2: headline2Style.copyWith(color: BrandColors.white),
|
caption: headline4Style.copyWith(color: BrandColors.white),
|
||||||
caption: headline4Style.copyWith(color: BrandColors.white),
|
bodyText1: body1Style.copyWith(color: BrandColors.white),
|
||||||
bodyText1: body1Style.copyWith(color: BrandColors.white),
|
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
|
||||||
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
labelStyle: TextStyle(color: BrandColors.white),
|
labelStyle: TextStyle(color: BrandColors.white),
|
||||||
|
|
|
@ -32,7 +32,8 @@ class HiveConfig {
|
||||||
await secureStorage.write(key: BNames.key, value: base64UrlEncode(key));
|
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!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,20 +3,20 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Localization extends StatelessWidget {
|
class Localization extends StatelessWidget {
|
||||||
const Localization({
|
const Localization({
|
||||||
Key key,
|
Key? key,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EasyLocalization(
|
return EasyLocalization(
|
||||||
preloaderColor: Colors.black,
|
|
||||||
supportedLocales: [Locale('ru'), Locale('en')],
|
supportedLocales: [Locale('ru'), Locale('en')],
|
||||||
path: 'assets/translations',
|
path: 'assets/translations',
|
||||||
fallbackLocale: Locale('en'),
|
fallbackLocale: Locale('ru'),
|
||||||
|
saveLocale: false,
|
||||||
useOnlyLangCode: true,
|
useOnlyLangCode: true,
|
||||||
child: child,
|
child: child!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
0
lib/config/md_files.dart
Normal file
|
@ -1,41 +1,38 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
|
||||||
import 'brand_colors.dart';
|
import 'brand_colors.dart';
|
||||||
|
|
||||||
final defaultTextStyle = GoogleFonts.inter(
|
final defaultTextStyle = TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: 15,
|
||||||
fontSize: 15,
|
color: BrandColors.textColor1,
|
||||||
color: BrandColors.textColor1,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline1Style = GoogleFonts.inter(
|
final headline1Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 40,
|
fontSize: 40,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline2Style = GoogleFonts.inter(
|
final headline2Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final onboardingTitle = GoogleFonts.inter(
|
final onboardingTitle = defaultTextStyle.copyWith(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline3Style = GoogleFonts.inter(
|
final headline3Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline4Style = GoogleFonts.inter(
|
final headline4Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: NamedFontWeight.medium,
|
fontWeight: NamedFontWeight.medium,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
|
@ -59,16 +56,12 @@ final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
|
||||||
|
|
||||||
final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue);
|
final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue);
|
||||||
|
|
||||||
final progressTextStyleLight = GoogleFonts.inter(
|
final progressTextStyleLight = TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: 11,
|
||||||
fontSize: 11,
|
color: BrandColors.textColor1,
|
||||||
color: BrandColors.textColor1,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final progressTextStyleDark = GoogleFonts.inter(
|
final progressTextStyleDark = TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: 11,
|
||||||
fontSize: 11,
|
color: BrandColors.white,
|
||||||
color: BrandColors.white,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,9 +18,9 @@ abstract class ApiMap {
|
||||||
};
|
};
|
||||||
loggedClient = client;
|
loggedClient = client;
|
||||||
}
|
}
|
||||||
String rootAddress;
|
String? rootAddress;
|
||||||
|
|
||||||
Dio loggedClient;
|
late Dio loggedClient;
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
loggedClient.close();
|
loggedClient.close();
|
||||||
|
@ -61,7 +61,7 @@ class ConsoleInterceptor extends InterceptorsWrapper {
|
||||||
addMessage(
|
addMessage(
|
||||||
Message.warn(
|
Message.warn(
|
||||||
text:
|
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);
|
return super.onError(err);
|
||||||
|
|
|
@ -3,17 +3,17 @@ import 'package:dio/dio.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
||||||
|
|
||||||
class BackblazeApi extends ApiMap {
|
class BackblazeApi extends ApiMap {
|
||||||
BackblazeApi([String token]) {
|
BackblazeApi([String? token]) {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
loggedClient.options = BaseOptions(
|
loggedClient.options = BaseOptions(
|
||||||
headers: {'Authorization': 'Basic $token'},
|
headers: {'Authorization': 'Basic $token'},
|
||||||
baseUrl: rootAddress,
|
baseUrl: rootAddress!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String rootAddress =
|
String? rootAddress =
|
||||||
'https://api.backblazeb2.com/b2api/v2/b2_authorize_account';
|
'https://api.backblazeb2.com/b2api/v2/b2_authorize_account';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
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) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/dns_records.dart';
|
import 'package:selfprivacy/logic/models/dns_records.dart';
|
||||||
|
|
||||||
class CloudflareApi extends ApiMap {
|
class CloudflareApi extends ApiMap {
|
||||||
CloudflareApi([String token]) {
|
CloudflareApi([String? token]) {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
loggedClient.options =
|
loggedClient.options =
|
||||||
BaseOptions(headers: {'Authorization': 'Bearer $token'});
|
BaseOptions(headers: {'Authorization': 'Bearer $token'});
|
||||||
|
@ -13,7 +13,7 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String rootAddress = 'https://api.cloudflare.com/client/v4';
|
String? rootAddress = 'https://api.cloudflare.com/client/v4';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(String token) async {
|
||||||
var url = '$rootAddress/user/tokens/verify';
|
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 url = '$rootAddress/zones';
|
||||||
|
|
||||||
var options = Options(
|
var options = Options(
|
||||||
|
@ -59,8 +59,8 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeSimilarRecords({
|
Future<void> removeSimilarRecords({
|
||||||
String ip4,
|
String? ip4,
|
||||||
CloudFlareDomain cloudFlareDomain,
|
required CloudFlareDomain cloudFlareDomain,
|
||||||
}) async {
|
}) async {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
var domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
var domainZoneId = cloudFlareDomain.zoneId;
|
||||||
|
@ -82,8 +82,8 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createMultipleDnsRecords({
|
Future<void> createMultipleDnsRecords({
|
||||||
String ip4,
|
String? ip4,
|
||||||
CloudFlareDomain cloudFlareDomain,
|
required CloudFlareDomain cloudFlareDomain,
|
||||||
}) async {
|
}) async {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
var domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
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 domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
|
||||||
|
|
||||||
var mx = DnsRecords(type: 'MX', name: '@', content: domainName);
|
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 url = '$rootAddress/zones?per_page=50';
|
||||||
var response = await loggedClient.get(
|
var response = await loggedClient.get(
|
||||||
url,
|
url,
|
||||||
|
@ -169,7 +169,7 @@ class CloudflareApi extends ApiMap {
|
||||||
);
|
);
|
||||||
|
|
||||||
return response.data['result']
|
return response.data['result']
|
||||||
.map<String>((el) => el['name'] as String)
|
.map<String>((el) => el['name'] as String?)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,23 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_details.dart';
|
import 'package:selfprivacy/logic/models/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
import 'package:selfprivacy/utils/password_generator2.dart';
|
import 'package:selfprivacy/utils/password_generator2.dart';
|
||||||
|
|
||||||
class HetznerApi extends ApiMap {
|
class HetznerApi extends ApiMap {
|
||||||
HetznerApi([String token]) {
|
HetznerApi([String? token]) {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
loggedClient.options = BaseOptions(
|
loggedClient.options = BaseOptions(
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
baseUrl: rootAddress,
|
baseUrl: rootAddress!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String rootAddress = 'https://api.hetzner.cloud/v1/servers';
|
String? rootAddress = 'https://api.hetzner.cloud/v1/servers';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(String token) async {
|
||||||
var options = Options(
|
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) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -41,9 +40,9 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> createServer({
|
Future<HetznerServerDetails> createServer({
|
||||||
@required String cloudFlareKey,
|
required String? cloudFlareKey,
|
||||||
@required User rootUser,
|
required User rootUser,
|
||||||
@required String domainName,
|
required String? domainName,
|
||||||
}) async {
|
}) async {
|
||||||
var dbPassword = getRandomString(40);
|
var dbPassword = getRandomString(40);
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ class HetznerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
|
|
||||||
Response response = await loggedClient.post(
|
Response response = await loggedClient.post(
|
||||||
rootAddress,
|
rootAddress!,
|
||||||
data: data,
|
data: data,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -64,17 +63,17 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteSelfprivacyServer({
|
Future<void> deleteSelfprivacyServer({
|
||||||
@required String cloudFlareKey,
|
required String? cloudFlareKey,
|
||||||
}) async {
|
}) async {
|
||||||
Response response = await loggedClient.get(rootAddress);
|
Response response = await loggedClient.get(rootAddress!);
|
||||||
|
|
||||||
List list = response.data['servers'];
|
List list = response.data['servers'];
|
||||||
var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server');
|
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({
|
Future<HetznerServerDetails> startServer({
|
||||||
HetznerServerDetails server,
|
required HetznerServerDetails server,
|
||||||
}) async {
|
}) async {
|
||||||
await loggedClient.post('/${server.id}/actions/poweron');
|
await loggedClient.post('/${server.id}/actions/poweron');
|
||||||
|
|
||||||
|
@ -84,7 +83,7 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> restart({
|
Future<HetznerServerDetails> restart({
|
||||||
HetznerServerDetails server,
|
required HetznerServerDetails server,
|
||||||
}) async {
|
}) async {
|
||||||
await loggedClient.post('/${server.id}/actions/poweron');
|
await loggedClient.post('/${server.id}/actions/poweron');
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:dio/dio.dart';
|
||||||
import 'api_map.dart';
|
import 'api_map.dart';
|
||||||
|
|
||||||
class ServerApi extends ApiMap {
|
class ServerApi extends ApiMap {
|
||||||
ServerApi(String domainName) {
|
ServerApi(String? domainName) {
|
||||||
loggedClient.options = BaseOptions(
|
loggedClient.options = BaseOptions(
|
||||||
baseUrl: 'https://api.$domainName',
|
baseUrl: 'https://api.$domainName',
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
|
|
||||||
|
@ -10,6 +9,7 @@ import 'package:selfprivacy/logic/models/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
|
|
||||||
import 'app_config_repository.dart';
|
import 'app_config_repository.dart';
|
||||||
|
export 'package:provider/provider.dart';
|
||||||
|
|
||||||
part 'app_config_state.dart';
|
part 'app_config_state.dart';
|
||||||
|
|
||||||
|
@ -64,23 +64,23 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void startServerIfDnsIsOkay({
|
void startServerIfDnsIsOkay({
|
||||||
AppConfigState state,
|
AppConfigState? state,
|
||||||
bool isImmediate = false,
|
bool isImmediate = false,
|
||||||
}) async {
|
}) async {
|
||||||
state = state ?? this.state;
|
state = state ?? this.state;
|
||||||
|
|
||||||
final work = () async {
|
final work = () async {
|
||||||
emit(TimerState(dataState: state, isLoading: true));
|
emit(TimerState(dataState: state!, isLoading: true));
|
||||||
|
|
||||||
var ip4 = state.hetznerServer.ip4;
|
var ip4 = state.hetznerServer!.ip4;
|
||||||
var domainName = state.cloudFlareDomain.domainName;
|
var domainName = state.cloudFlareDomain!.domainName;
|
||||||
|
|
||||||
var isMatch = await repository.isDnsAddressesMatch(domainName, ip4);
|
var isMatch = await repository.isDnsAddressesMatch(domainName, ip4);
|
||||||
|
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
var server = await repository.startServer(
|
var server = await repository.startServer(
|
||||||
state.hetznerKey,
|
state.hetznerKey,
|
||||||
state.hetznerServer,
|
state.hetznerServer!,
|
||||||
);
|
);
|
||||||
repository.saveServerDetails(server);
|
repository.saveServerDetails(server);
|
||||||
emit(
|
emit(
|
||||||
|
@ -111,16 +111,16 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetServerIfServerIsOkay({
|
void resetServerIfServerIsOkay({
|
||||||
AppConfigState state,
|
AppConfigState? state,
|
||||||
bool isImmediate = false,
|
bool isImmediate = false,
|
||||||
}) async {
|
}) async {
|
||||||
state = state ?? this.state;
|
state = state ?? this.state;
|
||||||
|
|
||||||
var work = () async {
|
var work = () async {
|
||||||
emit(TimerState(dataState: state, isLoading: true));
|
emit(TimerState(dataState: state!, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking(
|
var isServerWorking = await repository.isHttpServerWorking(
|
||||||
state.cloudFlareDomain.domainName,
|
state.cloudFlareDomain!.domainName,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
|
@ -133,8 +133,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
));
|
));
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
var hetznerServerDetails = await repository.restart(
|
var hetznerServerDetails = await repository.restart(
|
||||||
state.hetznerKey,
|
state!.hetznerKey,
|
||||||
state.hetznerServer,
|
state.hetznerServer!,
|
||||||
);
|
);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
|
@ -165,19 +165,19 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer timer;
|
Timer? timer;
|
||||||
|
|
||||||
void finishCheckIfServerIsOkay({
|
void finishCheckIfServerIsOkay({
|
||||||
AppConfigState state,
|
AppConfigState? state,
|
||||||
bool isImmediate = false,
|
bool isImmediate = false,
|
||||||
}) async {
|
}) async {
|
||||||
state = state ?? this.state;
|
state = state ?? this.state;
|
||||||
|
|
||||||
var work = () async {
|
var work = () async {
|
||||||
emit(TimerState(dataState: state, isLoading: true));
|
emit(TimerState(dataState: state!, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking(
|
var isServerWorking = await repository.isHttpServerWorking(
|
||||||
state.cloudFlareDomain.domainName,
|
state.cloudFlareDomain!.domainName,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
|
@ -238,12 +238,12 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void createServerAndSetDnsRecords() async {
|
void createServerAndSetDnsRecords() async {
|
||||||
var _stateCopy = state;
|
AppConfigState _stateCopy = state;
|
||||||
var onSuccess = (serverDetails) async {
|
var onSuccess = (serverDetails) async {
|
||||||
await repository.createDnsRecords(
|
await repository.createDnsRecords(
|
||||||
state.cloudFlareKey,
|
state.cloudFlareKey,
|
||||||
serverDetails.ip4,
|
serverDetails.ip4,
|
||||||
state.cloudFlareDomain,
|
state.cloudFlareDomain!,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
|
@ -259,8 +259,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
await repository.createServer(
|
await repository.createServer(
|
||||||
state.hetznerKey,
|
state.hetznerKey,
|
||||||
state.rootUser,
|
state.rootUser!,
|
||||||
state.cloudFlareDomain.domainName,
|
state.cloudFlareDomain!.domainName,
|
||||||
state.cloudFlareKey,
|
state.cloudFlareKey,
|
||||||
onCancel: onCancel,
|
onCancel: onCancel,
|
||||||
onSuccess: onSuccess,
|
onSuccess: onSuccess,
|
||||||
|
@ -277,54 +277,8 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closeTimer() {
|
void _closeTimer() {
|
||||||
if (timer != null && timer.isActive) {
|
if (timer != null && timer!.isActive) {
|
||||||
timer.cancel();
|
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);
|
|
||||||
// }
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> startServer(
|
Future<HetznerServerDetails> startServer(
|
||||||
String hetznerKey,
|
String? hetznerKey,
|
||||||
HetznerServerDetails hetznerServer,
|
HetznerServerDetails hetznerServer,
|
||||||
) async {
|
) async {
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
var hetznerApi = HetznerApi(hetznerKey);
|
||||||
|
@ -75,7 +75,7 @@ class AppConfigRepository {
|
||||||
await box.put(BNames.hetznerServer, serverDetails);
|
await box.put(BNames.hetznerServer, serverDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isDnsAddressesMatch(String domainName, String ip4) async {
|
Future<bool> isDnsAddressesMatch(String? domainName, String? ip4) async {
|
||||||
print(domainName);
|
print(domainName);
|
||||||
var addresses = <String>[
|
var addresses = <String>[
|
||||||
'$domainName',
|
'$domainName',
|
||||||
|
@ -116,12 +116,12 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createServer(
|
Future<void> createServer(
|
||||||
String hetznerKey,
|
String? hetznerKey,
|
||||||
User rootUser,
|
User rootUser,
|
||||||
String domainName,
|
String? domainName,
|
||||||
String cloudFlareKey, {
|
String? cloudFlareKey, {
|
||||||
void Function() onCancel,
|
void Function()? onCancel,
|
||||||
Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
|
required Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
|
||||||
}) async {
|
}) async {
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
var hetznerApi = HetznerApi(hetznerKey);
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class AppConfigRepository {
|
||||||
hetznerApi.close();
|
hetznerApi.close();
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response.data['error']['code'] == 'uniqueness_error') {
|
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
||||||
var nav = getIt.get<NavigationService>();
|
var nav = getIt.get<NavigationService>();
|
||||||
nav.showPopUpDialog(
|
nav.showPopUpDialog(
|
||||||
BrandAlert(
|
BrandAlert(
|
||||||
|
@ -165,7 +165,7 @@ class AppConfigRepository {
|
||||||
text: 'Отменить',
|
text: 'Отменить',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
hetznerApi.close();
|
hetznerApi.close();
|
||||||
onCancel();
|
onCancel!();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -176,8 +176,8 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createDnsRecords(
|
Future<void> createDnsRecords(
|
||||||
String cloudFlareKey,
|
String? cloudFlareKey,
|
||||||
String ip4,
|
String? ip4,
|
||||||
CloudFlareDomain cloudFlareDomain,
|
CloudFlareDomain cloudFlareDomain,
|
||||||
) async {
|
) async {
|
||||||
var cloudflareApi = CloudflareApi(cloudFlareKey);
|
var cloudflareApi = CloudflareApi(cloudFlareKey);
|
||||||
|
@ -195,7 +195,7 @@ class AppConfigRepository {
|
||||||
cloudflareApi.close();
|
cloudflareApi.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isHttpServerWorking(String domainName) async {
|
Future<bool> isHttpServerWorking(String? domainName) async {
|
||||||
var api = ServerApi(domainName);
|
var api = ServerApi(domainName);
|
||||||
var isHttpServerWorking = await api.isHttpServerWorking();
|
var isHttpServerWorking = await api.isHttpServerWorking();
|
||||||
api.close();
|
api.close();
|
||||||
|
@ -203,7 +203,7 @@ class AppConfigRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerDetails> restart(
|
Future<HetznerServerDetails> restart(
|
||||||
String hetznerKey,
|
String? hetznerKey,
|
||||||
HetznerServerDetails server,
|
HetznerServerDetails server,
|
||||||
) async {
|
) async {
|
||||||
var hetznerApi = HetznerApi(hetznerKey);
|
var hetznerApi = HetznerApi(hetznerKey);
|
||||||
|
|
|
@ -2,21 +2,21 @@ part of 'app_config_cubit.dart';
|
||||||
|
|
||||||
class AppConfigState extends Equatable {
|
class AppConfigState extends Equatable {
|
||||||
const AppConfigState({
|
const AppConfigState({
|
||||||
@required this.hetznerKey,
|
required this.hetznerKey,
|
||||||
@required this.cloudFlareKey,
|
required this.cloudFlareKey,
|
||||||
@required this.backblazeCredential,
|
required this.backblazeCredential,
|
||||||
@required this.cloudFlareDomain,
|
required this.cloudFlareDomain,
|
||||||
@required this.rootUser,
|
required this.rootUser,
|
||||||
@required this.hetznerServer,
|
required this.hetznerServer,
|
||||||
@required this.isServerStarted,
|
required this.isServerStarted,
|
||||||
@required this.isServerReseted,
|
required this.isServerReseted,
|
||||||
@required this.hasFinalChecked,
|
required this.hasFinalChecked,
|
||||||
@required this.isLoading,
|
required this.isLoading,
|
||||||
@required this.error,
|
required this.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object?> get props => [
|
||||||
hetznerKey,
|
hetznerKey,
|
||||||
cloudFlareKey,
|
cloudFlareKey,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
|
@ -30,31 +30,31 @@ class AppConfigState extends Equatable {
|
||||||
error,
|
error,
|
||||||
];
|
];
|
||||||
|
|
||||||
final String hetznerKey;
|
final String? hetznerKey;
|
||||||
final String cloudFlareKey;
|
final String? cloudFlareKey;
|
||||||
final BackblazeCredential backblazeCredential;
|
final BackblazeCredential? backblazeCredential;
|
||||||
final CloudFlareDomain cloudFlareDomain;
|
final CloudFlareDomain? cloudFlareDomain;
|
||||||
final User rootUser;
|
final User? rootUser;
|
||||||
final HetznerServerDetails hetznerServer;
|
final HetznerServerDetails? hetznerServer;
|
||||||
final bool isServerStarted;
|
final bool? isServerStarted;
|
||||||
final bool isServerReseted;
|
final bool? isServerReseted;
|
||||||
final bool hasFinalChecked;
|
final bool? hasFinalChecked;
|
||||||
|
|
||||||
final bool isLoading;
|
final bool? isLoading;
|
||||||
final Exception error;
|
final Exception? error;
|
||||||
|
|
||||||
AppConfigState copyWith({
|
AppConfigState copyWith({
|
||||||
String hetznerKey,
|
String? hetznerKey,
|
||||||
String cloudFlareKey,
|
String? cloudFlareKey,
|
||||||
BackblazeCredential backblazeCredential,
|
BackblazeCredential? backblazeCredential,
|
||||||
CloudFlareDomain cloudFlareDomain,
|
CloudFlareDomain? cloudFlareDomain,
|
||||||
User rootUser,
|
User? rootUser,
|
||||||
HetznerServerDetails hetznerServer,
|
HetznerServerDetails? hetznerServer,
|
||||||
bool isServerStarted,
|
bool? isServerStarted,
|
||||||
bool isServerReseted,
|
bool? isServerReseted,
|
||||||
bool hasFinalChecked,
|
bool? hasFinalChecked,
|
||||||
bool isLoading,
|
bool? isLoading,
|
||||||
Exception error,
|
Exception? error,
|
||||||
}) =>
|
}) =>
|
||||||
AppConfigState(
|
AppConfigState(
|
||||||
hetznerKey: hetznerKey ?? this.hetznerKey,
|
hetznerKey: hetznerKey ?? this.hetznerKey,
|
||||||
|
@ -77,10 +77,10 @@ class AppConfigState extends Equatable {
|
||||||
bool get isUserFilled => rootUser != null;
|
bool get isUserFilled => rootUser != null;
|
||||||
bool get isServerCreated => hetznerServer != null;
|
bool get isServerCreated => hetznerServer != null;
|
||||||
|
|
||||||
bool get isFullyInitilized => _fulfilementList.every((el) => el);
|
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
|
||||||
int get progress => _fulfilementList.where((el) => el).length;
|
int get progress => _fulfilementList.where((el) => el!).length;
|
||||||
|
|
||||||
List<bool> get _fulfilementList => [
|
List<bool?> get _fulfilementList => [
|
||||||
isHetznerFilled,
|
isHetznerFilled,
|
||||||
isCloudFlareFilled,
|
isCloudFlareFilled,
|
||||||
isBackblazeFilled,
|
isBackblazeFilled,
|
||||||
|
@ -112,10 +112,10 @@ class InitialAppConfigState extends AppConfigState {
|
||||||
|
|
||||||
class TimerState extends AppConfigState {
|
class TimerState extends AppConfigState {
|
||||||
TimerState({
|
TimerState({
|
||||||
@required this.dataState,
|
required this.dataState,
|
||||||
this.timerStart,
|
this.timerStart,
|
||||||
this.duration,
|
this.duration,
|
||||||
@required bool isLoading,
|
required bool isLoading,
|
||||||
}) : super(
|
}) : super(
|
||||||
hetznerKey: dataState.hetznerKey,
|
hetznerKey: dataState.hetznerKey,
|
||||||
cloudFlareKey: dataState.cloudFlareKey,
|
cloudFlareKey: dataState.cloudFlareKey,
|
||||||
|
@ -131,11 +131,11 @@ class TimerState extends AppConfigState {
|
||||||
);
|
);
|
||||||
|
|
||||||
final AppConfigState dataState;
|
final AppConfigState dataState;
|
||||||
final DateTime timerStart;
|
final DateTime? timerStart;
|
||||||
final Duration duration;
|
final Duration? duration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object?> get props => [
|
||||||
dataState,
|
dataState,
|
||||||
timerStart,
|
timerStart,
|
||||||
duration,
|
duration,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
|
@ -9,8 +8,8 @@ part 'app_settings_state.dart';
|
||||||
|
|
||||||
class AppSettingsCubit extends Cubit<AppSettingsState> {
|
class AppSettingsCubit extends Cubit<AppSettingsState> {
|
||||||
AppSettingsCubit({
|
AppSettingsCubit({
|
||||||
@required bool isDarkModeOn,
|
required bool isDarkModeOn,
|
||||||
@required bool isOnbordingShowing,
|
required bool isOnbordingShowing,
|
||||||
}) : super(
|
}) : super(
|
||||||
AppSettingsState(
|
AppSettingsState(
|
||||||
isDarkModeOn: isDarkModeOn,
|
isDarkModeOn: isDarkModeOn,
|
||||||
|
@ -21,15 +20,15 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
|
||||||
Box box = Hive.box(BNames.appSettings);
|
Box box = Hive.box(BNames.appSettings);
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
bool isDarkModeOn = box.get(BNames.isDarkModeOn);
|
bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
|
||||||
bool isOnbordingShowing = box.get(BNames.isOnbordingShowing);
|
bool? isOnbordingShowing = box.get(BNames.isOnbordingShowing);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isDarkModeOn: isDarkModeOn,
|
isDarkModeOn: isDarkModeOn,
|
||||||
isOnbordingShowing: isOnbordingShowing,
|
isOnbordingShowing: isOnbordingShowing,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDarkMode({@required bool isDarkModeOn}) {
|
void updateDarkMode({required bool isDarkModeOn}) {
|
||||||
box.put(BNames.isDarkModeOn, isDarkModeOn);
|
box.put(BNames.isDarkModeOn, isDarkModeOn);
|
||||||
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
|
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ part of 'app_settings_cubit.dart';
|
||||||
|
|
||||||
class AppSettingsState extends Equatable {
|
class AppSettingsState extends Equatable {
|
||||||
const AppSettingsState({
|
const AppSettingsState({
|
||||||
@required this.isDarkModeOn,
|
required this.isDarkModeOn,
|
||||||
@required this.isOnbordingShowing,
|
required this.isOnbordingShowing,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isDarkModeOn;
|
final bool isDarkModeOn;
|
||||||
|
|
|
@ -42,13 +42,14 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
FieldCubit<String> keyId;
|
// ignore: close_sinks
|
||||||
|
late final FieldCubit<String> keyId;
|
||||||
FieldCubit<String> applicationKey;
|
// ignore: close_sinks
|
||||||
|
late final FieldCubit<String> applicationKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
bool isKeyValid;
|
late bool isKeyValid;
|
||||||
try {
|
try {
|
||||||
String encodedApiKey = encodedBackblazeKey(
|
String encodedApiKey = encodedBackblazeKey(
|
||||||
keyId.state.value,
|
keyId.state.value,
|
||||||
|
|
|
@ -30,11 +30,11 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
FieldCubit<String> apiKey;
|
late final FieldCubit<String> apiKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
bool isKeyValid;
|
late bool isKeyValid;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||||
|
|
||||||
class DomainSetupCubit extends Cubit<DomainSetupState> {
|
class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
DomainSetupCubit(this.initializingCubit) : super(Initial()) {
|
DomainSetupCubit(this.initializingCubit) : super(Initial()) {
|
||||||
var token = (initializingCubit.state.cloudFlareKey);
|
var token = initializingCubit.state.cloudFlareKey;
|
||||||
|
|
||||||
assert(token != null, 'no cloudflare token');
|
assert(token != null, 'no cloudflare token');
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfigCubit initializingCubit;
|
AppConfigCubit initializingCubit;
|
||||||
CloudflareApi api;
|
late CloudflareApi api;
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
emit(Loading(LoadingTypes.loadingDomain));
|
emit(Loading(LoadingTypes.loadingDomain));
|
||||||
var list = await api.domainList();
|
var list = await (api.domainList() as Future<List<String>>);
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
emit(Empty());
|
emit(Empty());
|
||||||
} else if (list.length == 1) {
|
} else if (list.length == 1) {
|
||||||
|
|
|
@ -30,11 +30,12 @@ class HetznerFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
FieldCubit<String> apiKey;
|
// ignore: close_sinks
|
||||||
|
late final FieldCubit<String> apiKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
bool isKeyValid;
|
late bool isKeyValid;
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -46,9 +46,12 @@ class RootUserFormCubit extends FormCubit {
|
||||||
|
|
||||||
final AppConfigCubit initializingCubit;
|
final AppConfigCubit initializingCubit;
|
||||||
|
|
||||||
FieldCubit<String> userName;
|
// ignore: close_sinks
|
||||||
FieldCubit<String> password;
|
late final FieldCubit<String> userName;
|
||||||
FieldCubit<bool> isVisible;
|
// ignore: close_sinks
|
||||||
|
late final FieldCubit<String> password;
|
||||||
|
// ignore: close_sinks
|
||||||
|
late final FieldCubit<bool> isVisible;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class UserFormCubit extends FormCubit {
|
class UserFormCubit extends FormCubit {
|
||||||
UserFormCubit({
|
UserFormCubit({
|
||||||
this.usersCubit,
|
required this.usersCubit,
|
||||||
User user,
|
User? user,
|
||||||
}) {
|
}) {
|
||||||
var isEdit = user != null;
|
var isEdit = user != null;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class UserFormCubit extends FormCubit {
|
||||||
var passwordRegExp = RegExp(r"[\n\r\s]+");
|
var passwordRegExp = RegExp(r"[\n\r\s]+");
|
||||||
|
|
||||||
login = FieldCubit(
|
login = FieldCubit(
|
||||||
initalValue: isEdit ? user.login : '',
|
initalValue: isEdit ? user!.login : '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('required'),
|
RequiredStringValidation('required'),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
|
@ -25,7 +25,7 @@ class UserFormCubit extends FormCubit {
|
||||||
);
|
);
|
||||||
|
|
||||||
password = FieldCubit(
|
password = FieldCubit(
|
||||||
initalValue: isEdit ? user.password : genPass(),
|
initalValue: isEdit ? user!.password : genPass(),
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('required'),
|
RequiredStringValidation('required'),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
|
@ -42,15 +42,16 @@ class UserFormCubit extends FormCubit {
|
||||||
login: login.state.value,
|
login: login.state.value,
|
||||||
password: password.state.value,
|
password: password.state.value,
|
||||||
);
|
);
|
||||||
usersCubit.add(user);
|
usersCubit.addUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldCubit<String> login;
|
// ignore: close_sinks
|
||||||
FieldCubit<String> password;
|
late FieldCubit<String> login;
|
||||||
|
late FieldCubit<String> password;
|
||||||
|
|
||||||
void genNewPassword() {
|
void genNewPassword() {
|
||||||
password.externalSetValue(genPass());
|
password.externalSetValue(genPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
UsersCubit usersCubit;
|
late UsersCubit usersCubit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ class LegnthStringValidationWithLenghShowing extends ValidationModel<String> {
|
||||||
: super((n) => n.length != length, errorText);
|
: super((n) => n.length != length, errorText);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String check(String val) {
|
String? check(String val) {
|
||||||
var length = val.length;
|
var length = val.length;
|
||||||
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
|
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
|
||||||
return test(val) ? errorMassage : null;
|
return test(val) ? errorMassage : null;
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import 'package:bloc/bloc.dart';
|
// import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
// import 'package:equatable/equatable.dart';
|
||||||
import 'package:meta/meta.dart';
|
// import 'package:meta/meta.dart';
|
||||||
import 'package:selfprivacy/logic/models/service.dart';
|
// import 'package:selfprivacy/logic/models/service.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
// import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
// export 'package:provider/provider.dart';
|
||||||
export 'package:selfprivacy/logic/models/state_types.dart';
|
// export 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
part 'services_state.dart';
|
// part 'services_state.dart';
|
||||||
|
|
||||||
class ServicesCubit extends Cubit<ServicesState> {
|
// class ServicesCubit extends Cubit<ServicesState> {
|
||||||
ServicesCubit() : super(ServicesState(all));
|
// ServicesCubit() : super(ServicesState(all));
|
||||||
|
|
||||||
void connect(Service service) {
|
// void connect(Service service) {
|
||||||
var newState = state.updateElement(service, StateType.stable);
|
// var newState = state.updateElement(service, StateType.stable);
|
||||||
emit(newState);
|
// emit(newState);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
final all = ServiceTypes.values
|
// final all = ServiceTypes.values
|
||||||
.map(
|
// .map(
|
||||||
(type) => Service(
|
// (type) => Service(
|
||||||
state: StateType.uninitialized,
|
// state: StateType.uninitialized,
|
||||||
type: type,
|
// type: type,
|
||||||
),
|
// ),
|
||||||
)
|
// )
|
||||||
.toList();
|
// .toList();
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
part of 'services_cubit.dart';
|
// part of 'services_cubit.dart';
|
||||||
|
|
||||||
@immutable
|
// @immutable
|
||||||
class ServicesState extends Equatable{
|
// class ServicesState extends Equatable{
|
||||||
ServicesState(this.all);
|
// ServicesState(this.all);
|
||||||
|
|
||||||
final List<Service> all;
|
// final List<Service> all;
|
||||||
|
|
||||||
ServicesState updateElement(Service service, StateType newState) {
|
// ServicesState updateElement(Service service, StateType newState) {
|
||||||
var newList = [...all];
|
// var newList = [...all];
|
||||||
var index = newList.indexOf(service);
|
// var index = newList.indexOf(service);
|
||||||
newList[index] = service.updateState(newState);
|
// newList[index] = service.updateState(newState);
|
||||||
return ServicesState(newList);
|
// return ServicesState(newList);
|
||||||
}
|
// }
|
||||||
|
|
||||||
List<Service> get connected => all
|
// List<Service> get connected => all
|
||||||
.where((service) => service.state != StateType.uninitialized)
|
// .where((service) => service.state != StateType.uninitialized)
|
||||||
.toList();
|
// .toList();
|
||||||
|
|
||||||
List<Service> get uninitialized => all
|
// List<Service> get uninitialized => all
|
||||||
.where((service) => service.state == StateType.uninitialized)
|
// .where((service) => service.state == StateType.uninitialized)
|
||||||
.toList();
|
// .toList();
|
||||||
|
|
||||||
@override
|
// @override
|
||||||
List<Object> get props => all;
|
// List<Object> get props => all;
|
||||||
}
|
// }
|
||||||
|
|
|
@ -8,14 +8,14 @@ part 'users_state.dart';
|
||||||
class UsersCubit extends Cubit<UsersState> {
|
class UsersCubit extends Cubit<UsersState> {
|
||||||
UsersCubit() : super(UsersState([]));
|
UsersCubit() : super(UsersState([]));
|
||||||
|
|
||||||
void add(User user) {
|
void addUser(User user) {
|
||||||
var users = [...state.users];
|
var users = [...state.users];
|
||||||
users.add(user);
|
users.add(user);
|
||||||
|
|
||||||
emit(UsersState(users));
|
emit(UsersState(users));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(User user) {
|
void remove(User? user) {
|
||||||
var users = [...state.users];
|
var users = [...state.users];
|
||||||
users.remove(user);
|
users.remove(user);
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class NavigationService {
|
class NavigationService {
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
NavigatorState get navigator => navigatorKey.currentState;
|
NavigatorState? get navigator => navigatorKey.currentState;
|
||||||
|
|
||||||
void showPopUpDialog(AlertDialog dialog) {
|
void showPopUpDialog(AlertDialog dialog) {
|
||||||
final context = navigatorKey.currentState.overlay.context;
|
final context = navigatorKey.currentState!.overlay!.context;
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -9,10 +9,10 @@ class BackblazeCredential {
|
||||||
BackblazeCredential({this.keyId, this.applicationKey});
|
BackblazeCredential({this.keyId, this.applicationKey});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String keyId;
|
final String? keyId;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String applicationKey;
|
final String? applicationKey;
|
||||||
|
|
||||||
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class BackblazeCredential {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String encodedBackblazeKey(String keyId, String applicationKey) {
|
String encodedBackblazeKey(String? keyId, String? applicationKey) {
|
||||||
String _apiKey = '$keyId:$applicationKey';
|
String _apiKey = '$keyId:$applicationKey';
|
||||||
String encodedApiKey = base64.encode(utf8.encode(_apiKey));
|
String encodedApiKey = base64.encode(utf8.encode(_apiKey));
|
||||||
return encodedApiKey;
|
return encodedApiKey;
|
||||||
|
|
|
@ -17,8 +17,8 @@ class BackblazeCredentialAdapter extends TypeAdapter<BackblazeCredential> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return BackblazeCredential(
|
return BackblazeCredential(
|
||||||
keyId: fields[0] as String,
|
keyId: fields[0] as String?,
|
||||||
applicationKey: fields[1] as String,
|
applicationKey: fields[1] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ class CloudFlareDomain {
|
||||||
CloudFlareDomain({this.domainName, this.zoneId});
|
CloudFlareDomain({this.domainName, this.zoneId});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String domainName;
|
final String? domainName;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String zoneId;
|
final String? zoneId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
|
@ -17,8 +17,8 @@ class CloudFlareDomainAdapter extends TypeAdapter<CloudFlareDomain> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return CloudFlareDomain(
|
return CloudFlareDomain(
|
||||||
domainName: fields[0] as String,
|
domainName: fields[0] as String?,
|
||||||
zoneId: fields[1] as String,
|
zoneId: fields[1] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'dns_records.g.dart';
|
part 'dns_records.g.dart';
|
||||||
|
@ -6,17 +5,17 @@ part 'dns_records.g.dart';
|
||||||
@JsonSerializable(createToJson: true, createFactory: false)
|
@JsonSerializable(createToJson: true, createFactory: false)
|
||||||
class DnsRecords {
|
class DnsRecords {
|
||||||
DnsRecords({
|
DnsRecords({
|
||||||
@required this.type,
|
required this.type,
|
||||||
@required this.name,
|
required this.name,
|
||||||
@required this.content,
|
required this.content,
|
||||||
this.ttl = 3600,
|
this.ttl = 3600,
|
||||||
this.priority = 10,
|
this.priority = 10,
|
||||||
this.proxied = false,
|
this.proxied = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String type;
|
final String type;
|
||||||
final String name;
|
final String? name;
|
||||||
final String content;
|
final String? content;
|
||||||
final int ttl;
|
final int ttl;
|
||||||
final int priority;
|
final int priority;
|
||||||
final bool proxied;
|
final bool proxied;
|
||||||
|
|
|
@ -5,12 +5,12 @@ final formater = new DateFormat('hh:mm');
|
||||||
class Message {
|
class Message {
|
||||||
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
|
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
|
||||||
|
|
||||||
final String text;
|
final String? text;
|
||||||
final DateTime time;
|
final DateTime time;
|
||||||
final MessageType type;
|
final MessageType type;
|
||||||
String get timeString => formater.format(time);
|
String get timeString => formater.format(time);
|
||||||
|
|
||||||
static Message warn({String text}) => Message(
|
static Message warn({String? text}) => Message(
|
||||||
text: text,
|
text: text,
|
||||||
type: MessageType.warning,
|
type: MessageType.warning,
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ enum ProviderType {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProviderModel extends Equatable {
|
class ProviderModel extends Equatable {
|
||||||
const ProviderModel({this.state, this.type});
|
const ProviderModel({required this.state, required this.type});
|
||||||
|
|
||||||
final StateType state;
|
final StateType state;
|
||||||
final ProviderType type;
|
final ProviderType type;
|
||||||
|
@ -21,7 +21,7 @@ class ProviderModel extends Equatable {
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [state, type];
|
List<Object?> get props => [state, type];
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -31,10 +31,8 @@ class ProviderModel extends Equatable {
|
||||||
case ProviderType.domain:
|
case ProviderType.domain:
|
||||||
return BrandIcons.globe;
|
return BrandIcons.globe;
|
||||||
|
|
||||||
break;
|
|
||||||
case ProviderType.backup:
|
case ProviderType.backup:
|
||||||
return BrandIcons.save;
|
return BrandIcons.save;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'server_details.g.dart';
|
part 'server_details.g.dart';
|
||||||
|
@ -6,25 +5,25 @@ part 'server_details.g.dart';
|
||||||
@HiveType(typeId: 2)
|
@HiveType(typeId: 2)
|
||||||
class HetznerServerDetails {
|
class HetznerServerDetails {
|
||||||
HetznerServerDetails({
|
HetznerServerDetails({
|
||||||
@required this.ip4,
|
required this.ip4,
|
||||||
@required this.id,
|
required this.id,
|
||||||
@required this.createTime,
|
required this.createTime,
|
||||||
this.startTime,
|
this.startTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String ip4;
|
final String? ip4;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final int id;
|
final int? id;
|
||||||
|
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
final DateTime createTime;
|
final DateTime? createTime;
|
||||||
|
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
final DateTime startTime;
|
final DateTime? startTime;
|
||||||
|
|
||||||
HetznerServerDetails copyWith({DateTime startTime}) {
|
HetznerServerDetails copyWith({DateTime? startTime}) {
|
||||||
return HetznerServerDetails(
|
return HetznerServerDetails(
|
||||||
startTime: startTime ?? this.startTime,
|
startTime: startTime ?? this.startTime,
|
||||||
createTime: createTime,
|
createTime: createTime,
|
||||||
|
|
|
@ -17,10 +17,10 @@ class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return HetznerServerDetails(
|
return HetznerServerDetails(
|
||||||
ip4: fields[0] as String,
|
ip4: fields[0] as String?,
|
||||||
id: fields[1] as int,
|
id: fields[1] as int?,
|
||||||
createTime: fields[3] as DateTime,
|
createTime: fields[3] as DateTime?,
|
||||||
startTime: fields[2] as DateTime,
|
startTime: fields[2] as DateTime?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
class ServerStatus {
|
class ServerStatus {
|
||||||
final StatusTypes http;
|
final StatusTypes http;
|
||||||
final StatusTypes imap;
|
final StatusTypes imap;
|
||||||
final StatusTypes smtp;
|
final StatusTypes smtp;
|
||||||
|
|
||||||
ServerStatus({
|
ServerStatus({
|
||||||
@required this.http,
|
required this.http,
|
||||||
this.imap = StatusTypes.nodata,
|
this.imap = StatusTypes.nodata,
|
||||||
this.smtp = StatusTypes.nodata,
|
this.smtp = StatusTypes.nodata,
|
||||||
});
|
});
|
||||||
|
@ -20,7 +18,7 @@ class ServerStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusTypes statusTypeFromNumber(int number) {
|
StatusTypes statusTypeFromNumber(int? number) {
|
||||||
if (number == 0) {
|
if (number == 0) {
|
||||||
return StatusTypes.ok;
|
return StatusTypes.ok;
|
||||||
} else if (number == 1) {
|
} else if (number == 1) {
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import 'package:equatable/equatable.dart';
|
// import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
// import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
enum ServiceTypes {
|
// enum ServiceTypes {
|
||||||
messanger,
|
// messanger,
|
||||||
mail,
|
// mail,
|
||||||
passwordManager,
|
// passwordManager,
|
||||||
github,
|
// github,
|
||||||
cloud,
|
// cloud,
|
||||||
}
|
// }
|
||||||
|
|
||||||
class Service extends Equatable {
|
// class Service extends Equatable {
|
||||||
const Service({this.state, this.type});
|
// const Service({required this.state, required this.type});
|
||||||
|
|
||||||
final StateType state;
|
// final StateType state;
|
||||||
final ServiceTypes type;
|
// final ServiceTypes type;
|
||||||
|
|
||||||
Service updateState(StateType newState) => Service(
|
// Service updateState(StateType newState) => Service(
|
||||||
state: newState,
|
// state: newState,
|
||||||
type: type,
|
// type: type,
|
||||||
);
|
// );
|
||||||
|
|
||||||
@override
|
// @override
|
||||||
List<Object> get props => [state, type];
|
// List<Object?> get props => [state, type];
|
||||||
}
|
// }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:selfprivacy/utils/color_utils.dart';
|
import 'package:selfprivacy/utils/color_utils.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:selfprivacy/utils/crypto.dart';
|
import 'package:selfprivacy/utils/crypto.dart';
|
||||||
|
@ -11,18 +10,18 @@ part 'user.g.dart';
|
||||||
@HiveType(typeId: 1)
|
@HiveType(typeId: 1)
|
||||||
class User extends Equatable {
|
class User extends Equatable {
|
||||||
User({
|
User({
|
||||||
@required this.login,
|
required this.login,
|
||||||
@required this.password,
|
required this.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String login;
|
final String login;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String password;
|
final String password;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [login, password];
|
List<Object?> get props => [login, password];
|
||||||
|
|
||||||
Color get color => stringToColor(login);
|
Color get color => stringToColor(login);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ void main() async {
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
getItSetup();
|
getItSetup();
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
Localization(
|
Localization(
|
||||||
|
@ -35,7 +36,7 @@ void main() async {
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var appSettings = context.watch<AppSettingsCubit>().state;
|
AppSettingsState appSettings = context.watch<AppSettingsCubit>().state;
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
|
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
|
||||||
|
@ -50,12 +51,12 @@ class MyApp extends StatelessWidget {
|
||||||
home: appSettings.isOnbordingShowing
|
home: appSettings.isOnbordingShowing
|
||||||
? OnboardingPage(nextPage: InitializingPage())
|
? OnboardingPage(nextPage: InitializingPage())
|
||||||
: RootPage(),
|
: RootPage(),
|
||||||
builder: (BuildContext context, Widget widget) {
|
builder: (BuildContext context, Widget? widget) {
|
||||||
Widget error = Text('...rendering error...');
|
Widget error = Text('...rendering error...');
|
||||||
if (widget is Scaffold || widget is Navigator)
|
if (widget is Scaffold || widget is Navigator)
|
||||||
error = Scaffold(body: Center(child: error));
|
error = Scaffold(body: Center(child: error));
|
||||||
ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error;
|
ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error;
|
||||||
return widget;
|
return widget!;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,14 +3,14 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
class ActionButton extends StatelessWidget {
|
||||||
const ActionButton({
|
const ActionButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.text,
|
this.text,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.isRed = false,
|
this.isRed = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String text;
|
final String? text;
|
||||||
final bool isRed;
|
final bool isRed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -19,12 +19,12 @@ class ActionButton extends StatelessWidget {
|
||||||
|
|
||||||
return TextButton(
|
return TextButton(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text!,
|
||||||
style: isRed ? TextStyle(color: BrandColors.red1) : null,
|
style: isRed ? TextStyle(color: BrandColors.red1) : null,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
navigator.pop();
|
navigator.pop();
|
||||||
if (onPressed != null) onPressed();
|
if (onPressed != null) onPressed!();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandAlert extends AlertDialog {
|
class BrandAlert extends AlertDialog {
|
||||||
BrandAlert({
|
BrandAlert({
|
||||||
Key key,
|
Key? key,
|
||||||
String title,
|
String? title,
|
||||||
String contentText,
|
String? contentText,
|
||||||
List<Widget> acitons,
|
List<Widget>? acitons,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
key: key,
|
||||||
title: title != null ? Text(title) : null,
|
title: title != null ? Text(title) : null,
|
||||||
content: title != null ? Text(contentText) : null,
|
content: title != null ? Text(contentText!) : null,
|
||||||
actions: acitons,
|
actions: acitons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ enum BrandButtonTypes { rised, text, iconText }
|
||||||
|
|
||||||
class BrandButton {
|
class BrandButton {
|
||||||
static rised({
|
static rised({
|
||||||
Key key,
|
Key? key,
|
||||||
@required VoidCallback onPressed,
|
required VoidCallback? onPressed,
|
||||||
String title,
|
String? title,
|
||||||
Widget child,
|
Widget? child,
|
||||||
}) {
|
}) {
|
||||||
assert(title == null || child == null, 'required title or child');
|
assert(title == null || child == null, 'required title or 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({
|
static text({
|
||||||
Key key,
|
Key? key,
|
||||||
@required VoidCallback onPressed,
|
required VoidCallback onPressed,
|
||||||
@required String title,
|
required String title,
|
||||||
}) =>
|
}) =>
|
||||||
_TextButton(
|
_TextButton(
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -35,10 +35,10 @@ class BrandButton {
|
||||||
);
|
);
|
||||||
|
|
||||||
static iconText({
|
static iconText({
|
||||||
Key key,
|
Key? key,
|
||||||
@required VoidCallback onPressed,
|
required VoidCallback onPressed,
|
||||||
@required String title,
|
required String title,
|
||||||
@required Icon icon,
|
required Icon icon,
|
||||||
}) =>
|
}) =>
|
||||||
_IconTextButton(
|
_IconTextButton(
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -50,15 +50,15 @@ class BrandButton {
|
||||||
|
|
||||||
class _RisedButton extends StatelessWidget {
|
class _RisedButton extends StatelessWidget {
|
||||||
const _RisedButton({
|
const _RisedButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.title,
|
this.title,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String title;
|
final String? title;
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -88,13 +88,13 @@ class _RisedButton extends StatelessWidget {
|
||||||
|
|
||||||
class _TextButton extends StatelessWidget {
|
class _TextButton extends StatelessWidget {
|
||||||
const _TextButton({
|
const _TextButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.title,
|
this.title,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String title;
|
final String? title;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -106,7 +106,7 @@ class _TextButton extends StatelessWidget {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.all(12),
|
padding: EdgeInsets.all(12),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: BrandColors.blue,
|
color: BrandColors.blue,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
@ -120,12 +120,12 @@ class _TextButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IconTextButton 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);
|
: super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String title;
|
final String? title;
|
||||||
final Icon icon;
|
final Icon? icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -4,11 +4,11 @@ import 'package:selfprivacy/utils/extensions/elevation_extension.dart';
|
||||||
|
|
||||||
class BrandCard extends StatelessWidget {
|
class BrandCard extends StatelessWidget {
|
||||||
const BrandCard({
|
const BrandCard({
|
||||||
Key key,
|
Key? key,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class BrandDivider extends StatelessWidget {
|
class BrandDivider extends StatelessWidget {
|
||||||
const BrandDivider({Key key}) : super(key: key);
|
const BrandDivider({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -4,8 +4,8 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
|
||||||
class BrandHeader extends StatelessWidget {
|
class BrandHeader extends StatelessWidget {
|
||||||
const BrandHeader({
|
const BrandHeader({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.title,
|
required this.title,
|
||||||
this.hasBackButton = false,
|
this.hasBackButton = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/// Flutter icons BrandIcons
|
/// 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.
|
/// 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
|
/// To use this font, place it in your fonts/ directory and include the
|
||||||
|
@ -19,7 +19,7 @@ class BrandIcons {
|
||||||
BrandIcons._();
|
BrandIcons._();
|
||||||
|
|
||||||
static const _kFontFam = 'BrandIcons';
|
static const _kFontFam = 'BrandIcons';
|
||||||
static const _kFontPkg = null;
|
static const String? _kFontPkg = null;
|
||||||
|
|
||||||
static const IconData connection =
|
static const IconData connection =
|
||||||
IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
@ -39,14 +39,22 @@ class BrandIcons {
|
||||||
IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData check =
|
static const IconData check =
|
||||||
IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData webcam =
|
||||||
|
IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData refresh =
|
static const IconData refresh =
|
||||||
IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
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 =
|
static const IconData settings =
|
||||||
IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData share =
|
static const IconData share =
|
||||||
IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData triangle =
|
static const IconData triangle =
|
||||||
IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData engineer =
|
||||||
|
IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData server =
|
static const IconData server =
|
||||||
IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData box =
|
static const IconData box =
|
||||||
|
@ -59,8 +67,16 @@ class BrandIcons {
|
||||||
IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData upload =
|
static const IconData upload =
|
||||||
IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData github =
|
|
||||||
IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
|
||||||
static const IconData arrow_left =
|
static const IconData arrow_left =
|
||||||
IconData(0xe818, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
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);
|
||||||
}
|
}
|
||||||
|
|
70
lib/ui/components/brand_md/brand_md.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,11 @@ var navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
class BrandModalSheet extends StatelessWidget {
|
class BrandModalSheet extends StatelessWidget {
|
||||||
const BrandModalSheet({
|
const BrandModalSheet({
|
||||||
Key key,
|
Key? key,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DraggableScrollableSheet(
|
return DraggableScrollableSheet(
|
||||||
|
|
|
@ -5,21 +5,19 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class BrandSpanButton extends TextSpan {
|
class BrandSpanButton extends TextSpan {
|
||||||
BrandSpanButton({
|
BrandSpanButton({
|
||||||
@required String text,
|
required String text,
|
||||||
@required VoidCallback onTap,
|
required VoidCallback onTap,
|
||||||
TextStyle style,
|
TextStyle? style,
|
||||||
}) : assert(text != null),
|
}) : super(
|
||||||
assert(onTap != null),
|
|
||||||
super(
|
|
||||||
recognizer: TapGestureRecognizer()..onTap = onTap,
|
recognizer: TapGestureRecognizer()..onTap = onTap,
|
||||||
text: text,
|
text: text,
|
||||||
style: (style ?? TextStyle()).copyWith(color: BrandColors.blue),
|
style: (style ?? TextStyle()).copyWith(color: BrandColors.blue),
|
||||||
);
|
);
|
||||||
|
|
||||||
static link({
|
static link({
|
||||||
@required String text,
|
required String text,
|
||||||
String urlString,
|
String? urlString,
|
||||||
TextStyle style,
|
TextStyle? style,
|
||||||
}) =>
|
}) =>
|
||||||
BrandSpanButton(
|
BrandSpanButton(
|
||||||
text: text,
|
text: text,
|
||||||
|
|
|
@ -1,37 +1,38 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
final _kBottomTabBarHeight = 51;
|
final _kBottomTabBarHeight = 51;
|
||||||
|
|
||||||
class BrandTabBar extends StatefulWidget {
|
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
|
@override
|
||||||
_BrandTabBarState createState() => _BrandTabBarState();
|
_BrandTabBarState createState() => _BrandTabBarState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BrandTabBarState extends State<BrandTabBar> {
|
class _BrandTabBarState extends State<BrandTabBar> {
|
||||||
int currentIndex;
|
int? currentIndex;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
currentIndex = widget.controller.index;
|
currentIndex = widget.controller!.index;
|
||||||
widget.controller.addListener(_listener);
|
widget.controller!.addListener(_listener);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
_listener() {
|
_listener() {
|
||||||
if (currentIndex != widget.controller.index) {
|
if (currentIndex != widget.controller!.index) {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentIndex = widget.controller.index;
|
currentIndex = widget.controller!.index;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
widget.controller ?? widget.controller.removeListener(_listener);
|
widget.controller ?? widget.controller!.removeListener(_listener);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +51,10 @@ class _BrandTabBarState extends State<BrandTabBar> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
_getIconButton('Провайдеры', BrandIcons.server, 0),
|
_getIconButton('basis.providers'.tr(), BrandIcons.server, 0),
|
||||||
_getIconButton('Сервисы', BrandIcons.box, 1),
|
_getIconButton('basis.services'.tr(), BrandIcons.box, 1),
|
||||||
_getIconButton('Пользователи', BrandIcons.users, 2),
|
_getIconButton('basis.users'.tr(), BrandIcons.users, 2),
|
||||||
_getIconButton('Еще', BrandIcons.menu, 3),
|
_getIconButton('basis.more'.tr(), BrandIcons.menu, 3),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -68,7 +69,7 @@ class _BrandTabBarState extends State<BrandTabBar> {
|
||||||
var isActive = currentIndex == index;
|
var isActive = currentIndex == index;
|
||||||
var color = isActive ? acitivColor : BrandColors.inactive;
|
var color = isActive ? acitivColor : BrandColors.inactive;
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => widget.controller.animateTo(index),
|
onTap: () => widget.controller!.animateTo(index),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(6),
|
padding: EdgeInsets.all(6),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
|
|
|
@ -17,26 +17,26 @@ enum TextType {
|
||||||
class BrandText extends StatelessWidget {
|
class BrandText extends StatelessWidget {
|
||||||
const BrandText(
|
const BrandText(
|
||||||
this.text, {
|
this.text, {
|
||||||
Key key,
|
Key? key,
|
||||||
this.style,
|
this.style,
|
||||||
@required this.type,
|
required this.type,
|
||||||
this.overflow,
|
this.overflow,
|
||||||
this.softWrap,
|
this.softWrap,
|
||||||
this.textAlign,
|
this.textAlign,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String text;
|
final String? text;
|
||||||
final TextStyle style;
|
final TextStyle? style;
|
||||||
final TextType type;
|
final TextType type;
|
||||||
final TextOverflow overflow;
|
final TextOverflow? overflow;
|
||||||
final bool softWrap;
|
final bool? softWrap;
|
||||||
final TextAlign textAlign;
|
final TextAlign? textAlign;
|
||||||
|
|
||||||
factory BrandText.h1(
|
factory BrandText.h1(
|
||||||
String text, {
|
String? text, {
|
||||||
TextStyle style,
|
TextStyle? style,
|
||||||
TextOverflow overflow,
|
TextOverflow? overflow,
|
||||||
bool softWrap,
|
bool? softWrap,
|
||||||
}) =>
|
}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
|
@ -44,18 +44,18 @@ class BrandText extends StatelessWidget {
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory BrandText.onboardingTitle(String text, {TextStyle style}) =>
|
factory BrandText.onboardingTitle(String text, {TextStyle? style}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.onboardingTitle,
|
type: TextType.onboardingTitle,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.h2(String text, {TextStyle style}) => BrandText(
|
factory BrandText.h2(String? text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.h2,
|
type: TextType.h2,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.h3(String text, {TextStyle style, TextAlign textAlign}) =>
|
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.h3,
|
type: TextType.h3,
|
||||||
|
@ -63,35 +63,35 @@ class BrandText extends StatelessWidget {
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
);
|
);
|
||||||
factory BrandText.h4(String text, {TextStyle style}) => BrandText(
|
factory BrandText.h4(String? text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.h4,
|
type: TextType.h4,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.body1(String text, {TextStyle style}) => BrandText(
|
factory BrandText.body1(String? text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.body1,
|
type: TextType.body1,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.body2(String text, {TextStyle style}) => BrandText(
|
factory BrandText.body2(String? text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.body2,
|
type: TextType.body2,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.medium(String text,
|
factory BrandText.medium(String? text,
|
||||||
{TextStyle style, TextAlign textAlign}) =>
|
{TextStyle? style, TextAlign? textAlign}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.medium,
|
type: TextType.medium,
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
factory BrandText.small(String text, {TextStyle style}) => BrandText(
|
factory BrandText.small(String text, {TextStyle? style}) => BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.small,
|
type: TextType.small,
|
||||||
style: style,
|
style: style,
|
||||||
);
|
);
|
||||||
factory BrandText.buttonTitleText(String text, {TextStyle style}) =>
|
factory BrandText.buttonTitleText(String? text, {TextStyle? style}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.buttonTitleText,
|
type: TextType.buttonTitleText,
|
||||||
|
@ -153,7 +153,7 @@ class BrandText extends StatelessWidget {
|
||||||
style = style.merge(this.style);
|
style = style.merge(this.style);
|
||||||
}
|
}
|
||||||
return Text(
|
return Text(
|
||||||
text,
|
text!,
|
||||||
style: style,
|
style: style,
|
||||||
overflow: overflow,
|
overflow: overflow,
|
||||||
softWrap: softWrap,
|
softWrap: softWrap,
|
||||||
|
|
|
@ -6,21 +6,21 @@ import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
|
||||||
class BrandTimer extends StatefulWidget {
|
class BrandTimer extends StatefulWidget {
|
||||||
const BrandTimer({
|
const BrandTimer({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.startDateTime,
|
required this.startDateTime,
|
||||||
@required this.duration,
|
required this.duration,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final DateTime startDateTime;
|
final DateTime? startDateTime;
|
||||||
final Duration duration;
|
final Duration? duration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_BrandTimerState createState() => _BrandTimerState();
|
_BrandTimerState createState() => _BrandTimerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BrandTimerState extends State<BrandTimer> {
|
class _BrandTimerState extends State<BrandTimer> {
|
||||||
String _timeString;
|
String? _timeString;
|
||||||
Timer timer;
|
late Timer timer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -31,8 +31,8 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
_timerStart() {
|
_timerStart() {
|
||||||
_timeString = diffenceFromStart;
|
_timeString = diffenceFromStart;
|
||||||
timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
|
timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
|
||||||
var timePassed = DateTime.now().difference(widget.startDateTime);
|
var timePassed = DateTime.now().difference(widget.startDateTime!);
|
||||||
if (timePassed > widget.duration) {
|
if (timePassed > widget.duration!) {
|
||||||
t.cancel();
|
t.cancel();
|
||||||
} else {
|
} else {
|
||||||
_getTime();
|
_getTime();
|
||||||
|
@ -66,12 +66,12 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
String get diffenceFromStart =>
|
String get diffenceFromStart =>
|
||||||
_durationToString(DateTime.now().difference(widget.startDateTime));
|
_durationToString(DateTime.now().difference(widget.startDateTime!));
|
||||||
|
|
||||||
String _durationToString(Duration duration) {
|
String _durationToString(Duration duration) {
|
||||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||||
String twoDigitSeconds =
|
String twoDigitSeconds =
|
||||||
twoDigits(widget.duration.inSeconds - duration.inSeconds.remainder(60));
|
twoDigits(widget.duration!.inSeconds - duration.inSeconds.remainder(60));
|
||||||
|
|
||||||
return "$twoDigitSeconds cек";
|
return "$twoDigitSeconds cек";
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class DotsIndicator extends StatelessWidget {
|
class DotsIndicator extends StatelessWidget {
|
||||||
const DotsIndicator({
|
const DotsIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.activeIndex,
|
required this.activeIndex,
|
||||||
@required this.count,
|
required this.count,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final int activeIndex;
|
final int activeIndex;
|
||||||
|
|
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class BrandError extends StatelessWidget {
|
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 Object? error;
|
||||||
final StackTrace stackTrace;
|
final StackTrace? stackTrace;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -3,14 +3,14 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
class IconStatusMask extends StatelessWidget {
|
class IconStatusMask extends StatelessWidget {
|
||||||
IconStatusMask({this.child, this.status});
|
IconStatusMask({required this.child, required this.status});
|
||||||
final Icon child;
|
final Icon child;
|
||||||
|
|
||||||
final StateType status;
|
final StateType status;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
List<Color> colors;
|
late List<Color> colors;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case StateType.uninitialized:
|
case StateType.uninitialized:
|
||||||
colors = BrandColors.uninitializedGradientColors;
|
colors = BrandColors.uninitializedGradientColors;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
import 'package:selfprivacy/ui/pages/initializing/initializing.dart';
|
import 'package:selfprivacy/ui/pages/initializing/initializing.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class NotReadyCard extends StatelessWidget {
|
class NotReadyCard extends StatelessWidget {
|
||||||
const NotReadyCard({Key key}) : super(key: key);
|
const NotReadyCard({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -16,29 +18,34 @@ class NotReadyCard extends StatelessWidget {
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'Завершите настройку приложения используя ',
|
text: 'not_ready_card.1'.tr(),
|
||||||
style: TextStyle(color: BrandColors.white),
|
style: TextStyle(color: BrandColors.white),
|
||||||
),
|
),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: GestureDetector(
|
child: Padding(
|
||||||
child: Text(
|
padding: const EdgeInsets.only(bottom: 0.5),
|
||||||
'Мастер подключения',
|
child: GestureDetector(
|
||||||
style: TextStyle(
|
onTap: () => Navigator.of(context).push(
|
||||||
|
materialRoute(
|
||||||
|
InitializingPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'not_ready_card.2'.tr(),
|
||||||
|
style: body1Style.copyWith(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? Colors.blueAccent
|
? Colors.black
|
||||||
: BrandColors.white,
|
: BrandColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
decoration: TextDecoration.underline),
|
decoration: TextDecoration.underline,
|
||||||
),
|
// height: 1.1,
|
||||||
onTap: () => Navigator.of(context).push(
|
),
|
||||||
materialRoute(
|
|
||||||
InitializingPage(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' для продолжения работы',
|
text: 'not_ready_card.3'.tr(),
|
||||||
style: TextStyle(color: BrandColors.white),
|
style: TextStyle(color: BrandColors.white),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,9 +7,9 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
|
||||||
class ProgressBar extends StatefulWidget {
|
class ProgressBar extends StatefulWidget {
|
||||||
ProgressBar({
|
ProgressBar({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.steps,
|
required this.steps,
|
||||||
@required this.activeIndex,
|
required this.activeIndex,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final int activeIndex;
|
final int activeIndex;
|
||||||
|
@ -102,14 +102,14 @@ class _ProgressBarState extends State<ProgressBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Expanded _stepTitle({
|
Expanded _stepTitle({
|
||||||
int index,
|
required int index,
|
||||||
TextStyle style,
|
TextStyle? style,
|
||||||
String step,
|
String? step,
|
||||||
}) {
|
}) {
|
||||||
var isActive = index == widget.activeIndex;
|
var isActive = index == widget.activeIndex;
|
||||||
var checked = index < widget.activeIndex;
|
var checked = index < widget.activeIndex;
|
||||||
|
|
||||||
style = isActive ? style.copyWith(fontWeight: FontWeight.w700) : style;
|
style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style;
|
||||||
return Expanded(
|
return Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: RichText(
|
child: RichText(
|
||||||
|
|
|
@ -3,10 +3,10 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class SwitcherBlock extends StatelessWidget {
|
class SwitcherBlock extends StatelessWidget {
|
||||||
const SwitcherBlock({
|
const SwitcherBlock({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.child,
|
required this.child,
|
||||||
@required this.isActive,
|
required this.isActive,
|
||||||
@required this.onChange,
|
required this.onChange,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/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/backblaze_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/initializing/domain_cloudflare.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/logic/cubit/providers/providers_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.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_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.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/components/progress_bar/progress_bar.dart';
|
||||||
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class InitializingPage extends StatelessWidget {
|
class InitializingPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -60,9 +59,9 @@ class InitializingPage extends StatelessWidget {
|
||||||
'Domain',
|
'Domain',
|
||||||
'User',
|
'User',
|
||||||
'Server',
|
'Server',
|
||||||
'Check1',
|
' ✅',
|
||||||
'Check2',
|
' ✅',
|
||||||
'Check3'
|
' ✅'
|
||||||
],
|
],
|
||||||
activeIndex: cubit.state.progress,
|
activeIndex: cubit.state.progress,
|
||||||
),
|
),
|
||||||
|
@ -76,12 +75,13 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
title:
|
title: cubit.state.isFullyInitilized
|
||||||
cubit.state.isFullyInitilized ? 'Close' : 'Настрою потом',
|
? 'basis.close'.tr()
|
||||||
|
: 'Настрою потом',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pushAndRemoveUntil(
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
materialRoute(RootPage()),
|
materialRoute(RootPage()),
|
||||||
(predicate) => predicate == null,
|
(predicate) => false,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
|
@ -96,7 +96,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => HetznerFormCubit(initializingCubit),
|
create: (context) => HetznerFormCubit(initializingCubit),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var formCubit = context.watch<HetznerFormCubit>();
|
var formCubitState = context.watch<HetznerFormCubit>().state;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -105,13 +105,12 @@ class InitializingPage extends StatelessWidget {
|
||||||
width: 150,
|
width: 150,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.h2('Подключите сервер Hetzner'),
|
BrandText.h2('initializing.1'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body2(
|
BrandText.body2('initializing.2'.tr()),
|
||||||
'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
|
|
||||||
Spacer(),
|
Spacer(),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: formCubit.apiKey,
|
formFieldCubit: context.read<HetznerFormCubit>().apiKey,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -120,14 +119,15 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed: formCubitState.isSubmitting
|
||||||
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
|
? null
|
||||||
title: 'Подключить',
|
: () => context.read<HetznerFormCubit>().trySubmit(),
|
||||||
|
title: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Как получить API Token',
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -150,7 +150,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => CloudFlareFormCubit(initializingCubit),
|
create: (context) => CloudFlareFormCubit(initializingCubit),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var formCubit = context.watch<CloudFlareFormCubit>();
|
var formCubitState = context.watch<CloudFlareFormCubit>().state;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -160,28 +160,29 @@ class InitializingPage extends StatelessWidget {
|
||||||
width: 150,
|
width: 150,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.h2('Подключите CloudFlare'),
|
BrandText.h2('initializing.3'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body2('Для управления DNS вашего домена'),
|
BrandText.body2('initializing.4'.tr()),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: formCubit.apiKey,
|
formFieldCubit: context.read<CloudFlareFormCubit>().apiKey,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'CloudFlare API Token',
|
hintText: 'initializing.5'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed: formCubitState.isSubmitting
|
||||||
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
|
? null
|
||||||
title: 'Подключить',
|
: () => context.read<CloudFlareFormCubit>().trySubmit(),
|
||||||
|
title: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
title: 'Как получить API Token',
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -193,7 +194,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => BackblazeFormCubit(initializingCubit),
|
create: (context) => BackblazeFormCubit(initializingCubit),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var formCubit = context.watch<BackblazeFormCubit>();
|
var formCubitState = context.watch<BackblazeFormCubit>().state;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -202,11 +203,11 @@ class InitializingPage extends StatelessWidget {
|
||||||
height: 50,
|
height: 50,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.h2('Подключите облачное хранилище Backblaze'),
|
BrandText.h2('initializing.6'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: formCubit.keyId,
|
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -215,7 +216,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: formCubit.applicationKey,
|
formFieldCubit: context.read<BackblazeFormCubit>().applicationKey,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -224,14 +225,15 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed: formCubitState.isSubmitting
|
||||||
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
|
? null
|
||||||
title: 'Подключить',
|
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
||||||
|
title: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Как получить API Token',
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -243,8 +245,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DomainSetupCubit(initializingCubit)..load(),
|
create: (context) => DomainSetupCubit(initializingCubit)..load(),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var domainSetup = context.watch<DomainSetupCubit>();
|
DomainSetupState state = context.watch<DomainSetupCubit>().state;
|
||||||
var state = domainSetup.state;
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -253,19 +254,18 @@ class InitializingPage extends StatelessWidget {
|
||||||
width: 150,
|
width: 150,
|
||||||
),
|
),
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
BrandText.h2('Домен'),
|
BrandText.h2('basis.domain'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (state is Empty)
|
if (state is Empty) BrandText.body2('initializing.7'.tr()),
|
||||||
BrandText.body2('На данный момент подлюченных доменов нет'),
|
|
||||||
if (state is Loading)
|
if (state is Loading)
|
||||||
BrandText.body2(
|
BrandText.body2(
|
||||||
state.type == LoadingTypes.loadingDomain
|
state.type == LoadingTypes.loadingDomain
|
||||||
? 'Загружаем список доменов'
|
? 'initializing.8'.tr()
|
||||||
: 'Сохранение..',
|
: 'basis.saving'.tr(),
|
||||||
),
|
),
|
||||||
if (state is MoreThenOne)
|
if (state is MoreThenOne)
|
||||||
BrandText.body2(
|
BrandText.body2(
|
||||||
'Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены',
|
'initializing.9'.tr(),
|
||||||
),
|
),
|
||||||
if (state is Loaded) ...[
|
if (state is Loaded) ...[
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
|
@ -283,7 +283,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
Container(
|
Container(
|
||||||
width: 50,
|
width: 50,
|
||||||
child: BrandButton.rised(
|
child: BrandButton.rised(
|
||||||
onPressed: () => domainSetup.load(),
|
onPressed: () => context.read<DomainSetupCubit>().load(),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -302,7 +302,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
if (state is Empty) ...[
|
if (state is Empty) ...[
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: () => domainSetup.load(),
|
onPressed: () => context.read<DomainSetupCubit>().load(),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
@ -319,8 +319,8 @@ class InitializingPage extends StatelessWidget {
|
||||||
if (state is Loaded) ...[
|
if (state is Loaded) ...[
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: () => domainSetup.saveDomain(),
|
onPressed: () => context.read<DomainSetupCubit>().saveDomain(),
|
||||||
title: 'Сохранить домен',
|
title: 'initializing.10'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
|
@ -328,7 +328,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Как получить API Token',
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -340,7 +340,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => RootUserFormCubit(initializingCubit),
|
create: (context) => RootUserFormCubit(initializingCubit),
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
var formCubit = context.watch<RootUserFormCubit>();
|
var formCubitState = context.watch<RootUserFormCubit>().state;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -348,30 +348,33 @@ class InitializingPage extends StatelessWidget {
|
||||||
Spacer(),
|
Spacer(),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: formCubit.userName,
|
formFieldCubit: context.read<RootUserFormCubit>().userName,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Никнейм',
|
hintText: 'basis.nickname'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
|
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
|
||||||
cubit: formCubit.isVisible,
|
bloc: context.read<RootUserFormCubit>().isVisible,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
var isVisible = state.value;
|
var isVisible = state.value;
|
||||||
return CubitFormTextField(
|
return CubitFormTextField(
|
||||||
obscureText: !isVisible,
|
obscureText: !isVisible,
|
||||||
formFieldCubit: formCubit.password,
|
formFieldCubit: context.read<RootUserFormCubit>().password,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Пароль',
|
hintText: 'basis.password'.tr(),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isVisible ? Icons.visibility : Icons.visibility_off,
|
isVisible ? Icons.visibility : Icons.visibility_off,
|
||||||
),
|
),
|
||||||
onPressed: () => formCubit.isVisible.setValue(!isVisible),
|
onPressed: () => context
|
||||||
|
.read<RootUserFormCubit>()
|
||||||
|
.isVisible
|
||||||
|
.setValue(!isVisible),
|
||||||
),
|
),
|
||||||
suffixIconConstraints: BoxConstraints(minWidth: 60),
|
suffixIconConstraints: BoxConstraints(minWidth: 60),
|
||||||
prefixIconConstraints: BoxConstraints(maxWidth: 85),
|
prefixIconConstraints: BoxConstraints(maxWidth: 85),
|
||||||
|
@ -382,14 +385,15 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed: formCubitState.isSubmitting
|
||||||
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
|
? null
|
||||||
title: 'Подключить',
|
: () => context.read<RootUserFormCubit>().trySubmit(),
|
||||||
|
title: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Как получить API Token',
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -404,19 +408,19 @@ class InitializingPage extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Spacer(flex: 2),
|
Spacer(flex: 2),
|
||||||
BrandText.h2('Создать сервер'),
|
BrandText.h2('initializing.how'.tr()),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body2('Создать сервер'),
|
BrandText.body2('initializing.11'.tr()),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed:
|
onPressed:
|
||||||
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
|
isLoading! ? null : appConfigCubit.createServerAndSetDnsRecords,
|
||||||
title: isLoading ? 'loading' : 'Создать сервер',
|
title: isLoading ? 'loading' : 'initializing.11'.tr(),
|
||||||
),
|
),
|
||||||
Spacer(flex: 2),
|
Spacer(flex: 2),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Что это значит?',
|
title: 'initializing.what'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -427,13 +431,13 @@ class InitializingPage extends StatelessWidget {
|
||||||
assert(appConfigCubit.state is TimerState, 'wronge state');
|
assert(appConfigCubit.state is TimerState, 'wronge state');
|
||||||
var state = appConfigCubit.state as TimerState;
|
var state = appConfigCubit.state as TimerState;
|
||||||
|
|
||||||
String text;
|
String? text;
|
||||||
if (state.isServerReseted) {
|
if (state.isServerReseted!) {
|
||||||
text = 'Сервер презагружен, ждем последнюю проверку';
|
text = 'initializing.13'.tr();
|
||||||
} else if (state.isServerStarted) {
|
} else if (state.isServerStarted!) {
|
||||||
text = 'Cервер запушен, сейчас он будет проверен и перезагружен';
|
text = 'initializing.14'.tr();
|
||||||
} else if (state.isServerCreated) {
|
} else if (state.isServerCreated) {
|
||||||
text = 'Cервер создан, идет проверка ДНС адресов и запуск сервера';
|
text = 'initializing.15'.tr();
|
||||||
}
|
}
|
||||||
return Builder(builder: (context) {
|
return Builder(builder: (context) {
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -443,23 +447,23 @@ class InitializingPage extends StatelessWidget {
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.body2(text),
|
BrandText.body2(text),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (!state.isLoading)
|
if (!state.isLoading!)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
BrandText.body2('До следующей проверки: '),
|
BrandText.body2('initializing.16'.tr()),
|
||||||
BrandTimer(
|
BrandTimer(
|
||||||
startDateTime: state.timerStart,
|
startDateTime: state.timerStart,
|
||||||
duration: state.duration,
|
duration: state.duration,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (state.isLoading) BrandText.body2('Проверка'),
|
if (state.isLoading!) BrandText.body2('initializing.17'.tr()),
|
||||||
Spacer(
|
Spacer(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
),
|
),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => _showModal(context, _HowHetzner()),
|
onPressed: () => _showModal(context, _HowHetzner()),
|
||||||
title: 'Что это значит?',
|
title: 'initializing.what'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -477,58 +481,17 @@ class InitializingPage extends StatelessWidget {
|
||||||
|
|
||||||
class _HowHetzner extends StatelessWidget {
|
class _HowHetzner extends StatelessWidget {
|
||||||
const _HowHetzner({
|
const _HowHetzner({
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var isDark = Theme.of(context).brightness == Brightness.dark;
|
|
||||||
|
|
||||||
return BrandModalSheet(
|
return BrandModalSheet(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: brandPagePadding2,
|
padding: brandPagePadding1,
|
||||||
child: Column(
|
child: BrandMarkdown(
|
||||||
children: [
|
fileName: 'how_hetzner',
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,24 @@
|
||||||
import 'package:flutter/material.dart';
|
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_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 {
|
class AboutPage extends StatelessWidget {
|
||||||
const AboutPage({Key key}) : super(key: key);
|
const AboutPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
child: BrandHeader(title: 'О проекте', hasBackButton: true),
|
child: BrandHeader(
|
||||||
|
title: 'more.about_project'.tr(), hasBackButton: true),
|
||||||
preferredSize: Size.fromHeight(52),
|
preferredSize: Size.fromHeight(52),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: Container(
|
||||||
padding: brandPagePadding2,
|
child: BrandMarkdown(
|
||||||
children: [
|
fileName: 'about',
|
||||||
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),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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_header/brand_header.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class AppSettingsPage extends StatefulWidget {
|
class AppSettingsPage extends StatefulWidget {
|
||||||
const AppSettingsPage({Key key}) : super(key: key);
|
const AppSettingsPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AppSettingsPageState createState() => _AppSettingsPageState();
|
_AppSettingsPageState createState() => _AppSettingsPageState();
|
||||||
|
@ -20,16 +21,14 @@ class AppSettingsPage extends StatefulWidget {
|
||||||
class _AppSettingsPageState extends State<AppSettingsPage> {
|
class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var appSettings = context.watch<AppSettingsCubit>();
|
var isDarkModeOn = context.watch<AppSettingsCubit>().state.isDarkModeOn;
|
||||||
|
|
||||||
var isDarkModeOn = appSettings.state.isDarkModeOn;
|
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
child:
|
child:
|
||||||
BrandHeader(title: 'Настройки приложения', hasBackButton: true),
|
BrandHeader(title: 'more.settings'.tr(), hasBackButton: true),
|
||||||
preferredSize: Size.fromHeight(52),
|
preferredSize: Size.fromHeight(52),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
|
@ -57,8 +56,9 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
activeColor: BrandColors.green1,
|
activeColor: BrandColors.green1,
|
||||||
activeTrackColor: BrandColors.green2,
|
activeTrackColor: BrandColors.green2,
|
||||||
value: Theme.of(context).brightness == Brightness.dark,
|
value: Theme.of(context).brightness == Brightness.dark,
|
||||||
onChanged: (value) => appSettings.updateDarkMode(
|
onChanged: (value) => context
|
||||||
isDarkModeOn: !isDarkModeOn),
|
.read<AppSettingsCubit>()
|
||||||
|
.updateDarkMode(isDarkModeOn: !isDarkModeOn),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -80,8 +80,10 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
RaisedButton(
|
ElevatedButton(
|
||||||
color: BrandColors.red1,
|
style: ElevatedButton.styleFrom(
|
||||||
|
primary: BrandColors.red1,
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Reset',
|
'Reset',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -92,24 +94,26 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
child: BrandAlert(
|
builder: (_) {
|
||||||
title: 'Вы уверенны',
|
return BrandAlert(
|
||||||
contentText: 'Сбросить все ключи?',
|
title: 'Вы уверенны',
|
||||||
acitons: [
|
contentText: 'Сбросить все ключи?',
|
||||||
ActionButton(
|
acitons: [
|
||||||
text: 'Да, сбросить',
|
ActionButton(
|
||||||
isRed: true,
|
text: 'Да, сбросить',
|
||||||
onPressed: () {
|
isRed: true,
|
||||||
context
|
onPressed: () {
|
||||||
.read<AppConfigCubit>()
|
context
|
||||||
.clearAppConfig();
|
.read<AppConfigCubit>()
|
||||||
Navigator.of(context).pop();
|
.clearAppConfig();
|
||||||
}),
|
Navigator.of(context).pop();
|
||||||
ActionButton(
|
}),
|
||||||
text: 'Отмена',
|
ActionButton(
|
||||||
),
|
text: 'Отмена',
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -126,9 +130,9 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
|
|
||||||
class _TextColumn extends StatelessWidget {
|
class _TextColumn extends StatelessWidget {
|
||||||
const _TextColumn({
|
const _TextColumn({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.title,
|
required this.title,
|
||||||
@required this.value,
|
required this.value,
|
||||||
this.hasWarning = false,
|
this.hasWarning = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||||
|
|
||||||
class Console extends StatefulWidget {
|
class Console extends StatefulWidget {
|
||||||
const Console({Key key}) : super(key: key);
|
const Console({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ConsoleState createState() => _ConsoleState();
|
_ConsoleState createState() => _ConsoleState();
|
||||||
|
|