Compare commits
30 Commits
Author | SHA1 | Date |
---|---|---|
tester.nicolai | f8f43b6cee | |
Illia Chub | a7e7d0ff05 | |
Kherel | 4942f67f37 | |
Kherel | d0023e5718 | |
Illia Chub | e064598c73 | |
Kherel | 90d64d8f51 | |
Kherel | 26607251d9 | |
Kherel | 84e9259ec2 | |
Kherel | 94a0e22b15 | |
Kherel | 1a8a4e7270 | |
Kherel | 1202e4ad53 | |
Illia Chub | 8ea1b28d73 | |
Illia Chub | 8f8714e07f | |
Illia Chub | 63d3057125 | |
Kherel | dfc6f67ee3 | |
Illia Chub | b130960113 | |
Kherel | 5dea5234de | |
Kherel | 3a5353dbf4 | |
Kherel | 65c6a0b870 | |
Kherel | 933e8ffb90 | |
Kherel | 1c352fd771 | |
Kherel Kechil | f53ad044c1 | |
Kherel | 21611e63c7 | |
Kherel | d8393f75ea | |
Kherel | 9e8fdf2965 | |
Kherel | d3f494adeb | |
kherel | e849b551fc | |
Kherel | 234515477c | |
Kherel | e5758aa2bf | |
Kherel | d4f315214b |
|
@ -0,0 +1,16 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.dart]
|
||||
max_line_length = 150
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -1,12 +1,12 @@
|
|||
### О проекте
|
||||
|
||||
Всё больше организаций хотят владеть нашими данными
|
||||
А мы сами хотим распоряжаться своими **данными** на своем сервере.
|
||||
Проект позволяет только Вам в полной мере распоряжаться собственными **данными** на своём сервере.
|
||||
|
||||
### Миссия проекта
|
||||
|
||||
Цифровая независимость и приватность доступная каждому
|
||||
Цифровая независимость и приватность доступная каждому.
|
||||
|
||||
### Цель
|
||||
|
||||
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких
|
||||
Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких.
|
|
@ -3,5 +3,5 @@
|
|||
2. Заходим в созданный нами проект. Если такового - нет, значит создаём.
|
||||
3. Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).
|
||||
4. Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.
|
||||
5. В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.
|
||||
6. В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
|
||||
5. В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же Вы используете мобильную версию сайта, в нижнем правом углу Вы увидите красный плюсик. Нажимаем на эту кнопку.
|
||||
6. В поле Description, даём нашему токену название (это может быть любое название, которые Вам нравиться. Сути оно не меняет.
|
|
@ -19,12 +19,15 @@
|
|||
"connect": "Connect",
|
||||
"domain": "Domain",
|
||||
"saving": "Saving..",
|
||||
"nickname": "nickname",
|
||||
"nickname": "Nickname",
|
||||
"loading": "Loading...",
|
||||
"later": "I will setup it later",
|
||||
"reset": "Reset",
|
||||
"details": "Details",
|
||||
"no_data": "No data"
|
||||
"no_data": "No data",
|
||||
"wait": "Wait",
|
||||
"remove": "Remove",
|
||||
"apply": "Apply"
|
||||
},
|
||||
"more": {
|
||||
"_comment": "'More' tab",
|
||||
|
@ -32,9 +35,22 @@
|
|||
"about_project": "About us",
|
||||
"about_app": "About application",
|
||||
"onboarding": "Onboarding",
|
||||
"create_ssh_key": "Create ssh key",
|
||||
"generate_key": "Generate key",
|
||||
"generate_key_text": "You can generate ssh key",
|
||||
"console": "Console",
|
||||
"remove": "Remove",
|
||||
"enable": "Enable",
|
||||
"ok": "ok",
|
||||
"continue": "Continue",
|
||||
"ssh_key_exist_text": "You have generated ssh key",
|
||||
"yes_delete": "Yes, delete my SSH key",
|
||||
"share": "Share",
|
||||
"copy_buffer": "Copy to buffer",
|
||||
"copied_ssh": "SSH copied to clipboard",
|
||||
"delete_ssh_text": "Delete SSH key?",
|
||||
"about_app_page": {
|
||||
"text": "Тут любая служебная информация, v.{}"
|
||||
"text": "Application version v.{}"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Application settings",
|
||||
|
@ -145,6 +161,13 @@
|
|||
"bottom_sheet": {
|
||||
"1": "You can connect and create a new user here:"
|
||||
}
|
||||
},
|
||||
"vpn": {
|
||||
"title": "VPN Server",
|
||||
"subtitle": "Private VPN server",
|
||||
"bottom_sheet": {
|
||||
"1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure"
|
||||
}
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -188,8 +211,9 @@
|
|||
"20": "\n",
|
||||
"21": "One more restart to apply your security certificates.",
|
||||
"22": "Create master account",
|
||||
"23": "Enter a nickname and strong password"
|
||||
|
||||
"23": "Enter a nickname and strong password",
|
||||
"finish": "Everything is initialized",
|
||||
"checks": "Checks have been completed \n{} ouf of {}"
|
||||
},
|
||||
"modals": {
|
||||
"_comment": "messages in modals",
|
||||
|
@ -199,9 +223,29 @@
|
|||
"4": "Purge all authentication keys?",
|
||||
"5": "Yes, purge all my tokens",
|
||||
"6": "Delete the server and volume?",
|
||||
"7": "Yes"
|
||||
"7": "Yes",
|
||||
"8": "Remove task"
|
||||
},
|
||||
"timer": {
|
||||
"sec": "{} sec"
|
||||
},
|
||||
"jobs": {
|
||||
"_comment": "Jobs list",
|
||||
"title": "Jobs list",
|
||||
"start": "Start",
|
||||
"empty": "No jobs",
|
||||
"createUser": "Create",
|
||||
"serviceTurnOff": "Turn off",
|
||||
"serviceTurnOn": "Turn on",
|
||||
"jobAdded": "Job added",
|
||||
"runJobs": "Run jobs"
|
||||
},
|
||||
"validations": {
|
||||
"required": "Required",
|
||||
"invalid_format": "Invalid format",
|
||||
"root_name": "User name cannot be 'root'",
|
||||
"key_format": "Invalid key format",
|
||||
"length": "Length is [] shoud be {}",
|
||||
"user_alredy_exist": "Already exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"providers": "Провайдеры",
|
||||
"services": "Сервисы",
|
||||
"users": "Пользователи",
|
||||
"more": "Еще",
|
||||
"more": "Ещё",
|
||||
"next": "Далее",
|
||||
"got_it": "Понял",
|
||||
"settings": "Настройки",
|
||||
|
@ -22,26 +22,42 @@
|
|||
"nickname": "Никнейм",
|
||||
"loading": "Загрузка",
|
||||
"later": "Настрою потом",
|
||||
"reset": "Reset",
|
||||
"reset": "Сбросить",
|
||||
"details": "Детальная информация",
|
||||
"no_data": "Нет данных"
|
||||
"no_data": "Нет данных",
|
||||
"wait": "Загрузка",
|
||||
"remove": "Удалить",
|
||||
"apply": "Подать"
|
||||
},
|
||||
"more": {
|
||||
"_comment": "вкладка еще",
|
||||
"_comment": "вкладка ещё",
|
||||
"configuration_wizard": "Мастер Подключения",
|
||||
"about_project": "О проекте SelfPrivacy",
|
||||
"about_app": "О приложении",
|
||||
"onboarding": "Приветствие",
|
||||
"console": "Консоль",
|
||||
"create_ssh_key": "Создать ssh ключ",
|
||||
"generate_key": "Сгенерировать ключ",
|
||||
"generate_key_text": "Вы сможете сгенерировать ключ",
|
||||
"remove": "Отключить",
|
||||
"enable": "Включить",
|
||||
"ok": "ok",
|
||||
"continue": "Продолжить",
|
||||
"ssh_key_exist_text": "У Вас уже есть сгенерированный ssk ключ",
|
||||
"yes_delete": "Да, удалить",
|
||||
"share": "Поделиться",
|
||||
"copy_buffer": "Копировать в буфер",
|
||||
"copied_ssh": "SSH копировано в буфер",
|
||||
"delete_ssh_text": "Удалить SSH ключ?",
|
||||
"about_app_page": {
|
||||
"text": "Версия приложения: v.{}"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки приложения",
|
||||
"1": "Темная тема",
|
||||
"2": "Сменить цветовую тему",
|
||||
"1": "Тёмная тема",
|
||||
"2": "Сменить цветовую тему.",
|
||||
"3": "Сброс настроек",
|
||||
"4": "Сбросить API ключи а так же root пользвателя",
|
||||
"4": "Сбросить API ключи а также root пользвателя.",
|
||||
"5": "Удалить сервер",
|
||||
"6": "Действие приведет к удалению сервера. После чего он не будет доступен."
|
||||
}
|
||||
|
@ -49,9 +65,9 @@
|
|||
"onboarding": {
|
||||
"_comment": "страницы онбординга",
|
||||
"page1_title": "Цифровая независимость доступна каждому",
|
||||
"page1_text": "Почта, VPN, Мессенджер, социальная сеть и многое другое на вашем личном сервере, под вашим полным контролем.",
|
||||
"page2_title": "SelfPrivacy — это не облако, а ваш личный дата-центр",
|
||||
"page2_text": "У SelfPrivacy работает только с вашими сервис-провадерами: Hetzner, Cloudflare, Backblaze. Если у вас нет учетных записей, мы поможем их создать."
|
||||
"page1_text": "Почта, VPN, Мессенджер, социальная сеть и многое другое на Вашем личном сервере, под Вашим полным контролем.",
|
||||
"page2_title": "SelfPrivacy — это не облако, а Ваш личный дата-центр",
|
||||
"page2_text": "SelfPrivacy работает только с вашими сервис-провайдерами: Hetzner, Cloudflare, Backblaze. Если у Вас нет учётных записей, мы поможем их создать."
|
||||
},
|
||||
"providers": {
|
||||
"_comment": "вкладка провайдеры",
|
||||
|
@ -60,7 +76,7 @@
|
|||
"card_title": "Сервер",
|
||||
"status": "Статус — в норме",
|
||||
"bottom_sheet": {
|
||||
"1": "Это виртульный компьютер на котором работают все ваши сервисы.",
|
||||
"1": "Это виртуальный компьютер на котором работают все Ваши сервисы.",
|
||||
"2": "Общая информация",
|
||||
"3": "Размещение"
|
||||
},
|
||||
|
@ -74,16 +90,18 @@
|
|||
"card_title": "Домен",
|
||||
"status": "Статус — в норме",
|
||||
"bottom_sheet": {
|
||||
"1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.",
|
||||
"2": "{} — продлен до {}"
|
||||
"1": "Это Ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.",
|
||||
"2": "{} — продлен до {}."
|
||||
}
|
||||
},
|
||||
"backup": {
|
||||
"card_title": "Резервное копирование",
|
||||
"status": "Статус — в норме",
|
||||
"bottom_sheet": {
|
||||
"1": "Выручит в любой ситуации: хакерская атака, удаление сервера и т.п.",
|
||||
"2": "3Gb — бестплатно до 10Gb, последний вчера в {}"
|
||||
"1": "Выручит Вас в любой ситуации: хакерская атака, удаление сервера и.т.д.",
|
||||
"2": "Использовано 3Gb из бестплатых 10Gb.",
|
||||
"3": "Последнее копирование была сделана вчера в {}."
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -95,72 +113,72 @@
|
|||
},
|
||||
"services": {
|
||||
"_comment": "Вкладка сервисы",
|
||||
"title": "Ваши личные приватные и независимые сервисы",
|
||||
"title": "Ваши личные, приватные и независимые сервисы:",
|
||||
"mail": {
|
||||
"title": "Почта",
|
||||
"subtitle": "Электронная почта для семьи или компании",
|
||||
"subtitle": "Электронная почта для семьи или компании.",
|
||||
"bottom_sheet": {
|
||||
"1": "Для подключения почтового ящика используйте {} и логин пароль, который вы создали. Так же приглашайте",
|
||||
"2": "новых пользователей"
|
||||
"1": "Для подключения почтового ящика используйте {} и профиль, который Вы создали. Так же приглашайте",
|
||||
"2": "новых пользователей."
|
||||
}
|
||||
},
|
||||
"messenger": {
|
||||
"title": "Мессенджер",
|
||||
"subtitle": "Telegram и Signal не так приватны, как Delta.Chat использующий ваш личный сервер.",
|
||||
"subtitle": "Telegram и Signal не так приватны, как Delta.Chat — который использует Ваш личный сервер.",
|
||||
"bottom_sheet": {
|
||||
"1": "Для подключения используйте {} и логин пароль, который вы создали"
|
||||
"1": "Для подключения используйте {} и логин пароль, который Вы создали."
|
||||
}
|
||||
},
|
||||
"password_manager": {
|
||||
"title": "Менеджер паролей",
|
||||
"subtitle": "Фундамент безопасности. Создавать, хранить, копировать пароли между устройствами, вбивать их в формы поможет — Bitwarden.",
|
||||
"subtitle": "Это фундамент Вашей безопасности. Создавать, хранить, копировать пароли между устройствами и вбивать их в формы поможет — Bitwarden.",
|
||||
"bottom_sheet": {
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:."
|
||||
}
|
||||
},
|
||||
"video": {
|
||||
"title": "Видеоконференция",
|
||||
"subtitle": "Zoom и Google meet отличные инструменты, но Jitsi meet не хуже и дает уверенность, что вас никто не подслушивает.",
|
||||
"subtitle": "Jitsi meet — отличный аналог Zoom и Google meet который по мимо удобства ещё и гарантирует Вам защищённые высококачественные видеоконференции.",
|
||||
"bottom_sheet": {
|
||||
"1": "Для использования просто перейдите по ссылке:"
|
||||
"1": "Для использования просто перейдите по ссылке:."
|
||||
}
|
||||
},
|
||||
"cloud": {
|
||||
"title": "Файловое облако",
|
||||
"subtitle": "Не позволяйте облачным сервисам читать ваши данные используйте NextCloud.",
|
||||
"subtitle": "Не позволяйте облачным сервисам просматривать ваши данные. Используйте NextCloud — надёжный дом для всех Ваших данных.",
|
||||
"bottom_sheet": {
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:."
|
||||
}
|
||||
},
|
||||
"social_network": {
|
||||
"title": "Социальная сеть",
|
||||
"subtitle": "Сложно поверить, но стало возможным создать свою собственную социальную сеть, со своими правилами и аудиторией.",
|
||||
"bottom_sheet": {
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:."
|
||||
}
|
||||
},
|
||||
"git": {
|
||||
"title": "Git-сервер",
|
||||
"subtitle": "Приватная альтернатива Github, которая принадлежит вам, а не Microsoft.",
|
||||
"bottom_sheet": {
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:"
|
||||
"1": "Подключиться к серверу и создать пользователя можно по адресу:."
|
||||
}
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"_comment": "'Users' tab",
|
||||
"add_new_user": "Добавьте первого пользователя",
|
||||
"add_new_user": "Добавьте первого пользователя.",
|
||||
"new_user": "Новый пользователь",
|
||||
"not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя",
|
||||
"nobody_here": "'Здесь пока никого'",
|
||||
"nobody_here": "Здесь будут отображаться пользователи.",
|
||||
"login": "Логин",
|
||||
"onboarding": "Приветствие",
|
||||
"console": "Консоль разработчика",
|
||||
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.",
|
||||
"delete_confirm_question": "удалить учетную запись?",
|
||||
"new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам.",
|
||||
"delete_confirm_question": "Вы действительно хотите удалить учетную запись?",
|
||||
"reset_password": "Сбросить пароль",
|
||||
"account": "Учетная запись",
|
||||
"send_regisration_data": "Отправить реквизиты для входа"
|
||||
"send_regisration_data": "Поделиться реквизитами"
|
||||
},
|
||||
"initializing": {
|
||||
"_comment": "initializing page",
|
||||
|
@ -173,34 +191,56 @@
|
|||
"6": "Подключите облачное хранилище Backblaze",
|
||||
"7": "На данный момент подлюченных доменов нет",
|
||||
"8": "Загружаем список доменов",
|
||||
"9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены",
|
||||
"9": "Найдено больше одного домена, для вашей безопастности, просим Вам удалить не нужные домены",
|
||||
"10": "Сохранить домен",
|
||||
"final": "Последний шаг",
|
||||
"11": "Создать сервер",
|
||||
"what": "Что это значит?",
|
||||
"13": "Сервер презагружен, ждем последнюю проверку",
|
||||
"14": "Cервер запушен, сейчас он будет проверен и перезагружен",
|
||||
"15": "Cервер создан, идет проверка ДНС адресов и запуск сервера",
|
||||
"13": "Сервер презагружен, ждем последнюю проверку.",
|
||||
"14": "Cервер запущен, сейчас он будет проверен и перезагружен.",
|
||||
"15": "Cервер создан, идет проверка ДНС адресов и запуск сервера.",
|
||||
"16": "До следующей проверки: ",
|
||||
"17": "Проверка",
|
||||
"18": "Как получить Hetzner API Token'",
|
||||
"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, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.",
|
||||
"20": "\n2 Заходим в созданный нами проект. Если такового нет - значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же Вы используете мобильную версию сайта - в нижнем правом углу Вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description даём нашему токену название (это может быть любое название, которое Вам нравиться, сути оно не меняет.",
|
||||
"21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности",
|
||||
"22": "Создайте главную учетную запись",
|
||||
"23": "Введите никнейм и сложный пароль"
|
||||
"23": "Введите никнейм и сложный пароль",
|
||||
"finish": "Всё инициализировано.",
|
||||
"checks": "Проверок выполнено: \n{} / {}"
|
||||
},
|
||||
"modals": {
|
||||
"_comment": "messages in modals",
|
||||
"1": "Сервер с таким именем уже существует",
|
||||
"2": "Уничтожить сервер и создать новый?",
|
||||
"3": "Вы уверенны",
|
||||
"3": "Подтвердите",
|
||||
"4": "Сбросить все ключи?",
|
||||
"5": "Да, сбросить",
|
||||
"6": "Удалить сервер и диск?",
|
||||
"7": "Да, удалить"
|
||||
"7": "Да, удалить",
|
||||
"8": "Удалить задачу"
|
||||
},
|
||||
"timer": {
|
||||
"sec": "{} сек"
|
||||
},
|
||||
"jobs": {
|
||||
"_comment": "Jobs list",
|
||||
"title": "Задачи",
|
||||
"start": "Начать выполенение",
|
||||
"empty": "Пусто.",
|
||||
"createUser": "Создать запись",
|
||||
"serviceTurnOff": "Остановить",
|
||||
"serviceTurnOn": "Запустить",
|
||||
"jobAdded": "Задача добавленна",
|
||||
"runJobs": "Запустите задачи"
|
||||
},
|
||||
"validations": {
|
||||
"required": "Обязательное поле.",
|
||||
"invalid_format": "Неверный формат.",
|
||||
"root_name": "Имя пользователя не может быть'root'.",
|
||||
"key_format": "Неверный формат.",
|
||||
"length": "Длина строки [] должна быть {}.",
|
||||
"user_alredy_exist": "Имя уже используется."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
- Added users creation possibility
|
||||
- Fixed reverse DNS issue during server creation
|
||||
- Updated NixOS to 21.05
|
||||
- Defined application users as system ones
|
||||
- Minor bugfixes
|
||||
- Translation fixes
|
||||
- en_GB is now a separate locale
|
|
@ -1,5 +1,5 @@
|
|||
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://selfprivacy.org/en/second.html
|
||||
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
|
||||
|
@ -12,5 +12,4 @@ Application will do the following things for you:
|
|||
* 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
|
||||
!!! Project is currently in open beta state. Feel free to try it. It would be much appreciated if you would provide us with some feedback.
|
|
@ -21,6 +21,6 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -2,10 +2,14 @@ PODS:
|
|||
- Flutter (1.0.0)
|
||||
- flutter_secure_storage (3.3.1):
|
||||
- Flutter
|
||||
- local_auth (0.0.1):
|
||||
- Flutter
|
||||
- package_info (0.0.1):
|
||||
- Flutter
|
||||
- path_provider (0.0.1):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences (0.0.1):
|
||||
- Flutter
|
||||
- url_launcher (0.0.1):
|
||||
|
@ -16,8 +20,10 @@ PODS:
|
|||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- local_auth (from `.symlinks/plugins/local_auth/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
|
@ -27,10 +33,14 @@ EXTERNAL SOURCES:
|
|||
:path: Flutter
|
||||
flutter_secure_storage:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
local_auth:
|
||||
:path: ".symlinks/plugins/local_auth/ios"
|
||||
package_info:
|
||||
:path: ".symlinks/plugins/package_info/ios"
|
||||
path_provider:
|
||||
:path: ".symlinks/plugins/path_provider/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||
url_launcher:
|
||||
|
@ -39,10 +49,12 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
|
|
|
@ -361,6 +361,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = UVNTKR53DD;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -499,6 +500,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = UVNTKR53DD;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -529,6 +531,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = UVNTKR53DD;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 608 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 906 B After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 1007 B |
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 608 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
|
||||
class BlocAndProviderConfig extends StatelessWidget {
|
||||
|
@ -12,11 +14,9 @@ class BlocAndProviderConfig extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// var platformBrightness =
|
||||
// SchedulerBinding.instance.window.platformBrightness;
|
||||
// var isDark = platformBrightness == Brightness.dark;
|
||||
var isDark = false;
|
||||
|
||||
var usersCubit = UsersCubit();
|
||||
var servicesCubit = ServicesCubit();
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
|
@ -27,10 +27,17 @@ class BlocAndProviderConfig extends StatelessWidget {
|
|||
),
|
||||
BlocProvider(
|
||||
lazy: false,
|
||||
create: (_) => AppConfigCubit()..load(),
|
||||
create: (_) => AppConfigCubit(servicesCubit)..load(),
|
||||
),
|
||||
BlocProvider(create: (_) => ProvidersCubit()),
|
||||
BlocProvider(create: (_) => UsersCubit()),
|
||||
BlocProvider(create: (_) => usersCubit..load(), lazy: false),
|
||||
BlocProvider(create: (_) => servicesCubit..load(), lazy: false),
|
||||
BlocProvider(
|
||||
create: (_) => JobsCubit(
|
||||
usersCubit: usersCubit,
|
||||
servicesCubit: servicesCubit,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
final shadow8 = BoxShadow(
|
||||
offset: Offset(0, 4),
|
||||
blurRadius: 8,
|
||||
color: Colors.black.withOpacity(.08),
|
||||
);
|
|
@ -76,6 +76,6 @@ var darkTheme = ligtTheme.copyWith(
|
|||
),
|
||||
);
|
||||
|
||||
final brandPagePadding1 = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
|
||||
final paddingH15V30 = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
|
||||
|
||||
final brandPagePadding2 = EdgeInsets.symmetric(horizontal: 15);
|
||||
final paddingH15V0 = EdgeInsets.symmetric(horizontal: 15);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:get_it/get_it.dart';
|
|||
import 'package:selfprivacy/logic/get_it/api_config.dart';
|
||||
import 'package:selfprivacy/logic/get_it/console.dart';
|
||||
import 'package:selfprivacy/logic/get_it/navigation.dart';
|
||||
import 'package:selfprivacy/logic/get_it/ssh.dart';
|
||||
import 'package:selfprivacy/logic/get_it/timer.dart';
|
||||
|
||||
export 'package:selfprivacy/logic/get_it/api_config.dart';
|
||||
|
@ -9,7 +10,6 @@ export 'package:selfprivacy/logic/get_it/console.dart';
|
|||
export 'package:selfprivacy/logic/get_it/navigation.dart';
|
||||
export 'package:selfprivacy/logic/get_it/timer.dart';
|
||||
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
Future<void> getItSetup() async {
|
||||
|
@ -17,6 +17,7 @@ Future<void> getItSetup() async {
|
|||
|
||||
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
|
||||
getIt.registerSingleton<TimerModel>(TimerModel());
|
||||
getIt.registerSingleton<SSHModel>(SSHModel()..init());
|
||||
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
|
||||
|
||||
await getIt.allReady();
|
||||
|
|
|
@ -19,21 +19,25 @@ class HiveConfig {
|
|||
Hive.registerAdapter(HetznerDataBaseAdapter());
|
||||
|
||||
await Hive.openBox(BNames.appSettings);
|
||||
var cipher = HiveAesCipher(await getEncriptedKey());
|
||||
await Hive.openBox<User>(BNames.users);
|
||||
await Hive.openBox(BNames.servicesState);
|
||||
|
||||
var cipher = HiveAesCipher(await getEncriptedKey(BNames.key));
|
||||
await Hive.openBox(BNames.appConfig, encryptionCipher: cipher);
|
||||
|
||||
var sshCipher = HiveAesCipher(await getEncriptedKey(BNames.sshEnckey));
|
||||
await Hive.openBox(BNames.sshConfig, encryptionCipher: sshCipher);
|
||||
}
|
||||
|
||||
static Future<Uint8List> getEncriptedKey() async {
|
||||
final FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
||||
var containsEncryptionKey =
|
||||
await secureStorage.containsKey(key: BNames.key);
|
||||
if (!containsEncryptionKey) {
|
||||
static Future<Uint8List> getEncriptedKey(String encKey) async {
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
var hasEncryptionKey = await secureStorage.containsKey(key: encKey);
|
||||
if (!hasEncryptionKey) {
|
||||
var key = Hive.generateSecureKey();
|
||||
await secureStorage.write(key: BNames.key, value: base64UrlEncode(key));
|
||||
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
|
||||
}
|
||||
|
||||
String? string = await secureStorage.read(key: BNames.key);
|
||||
String? string = await secureStorage.read(key: encKey);
|
||||
return base64Url.decode(string!);
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +46,13 @@ class BNames {
|
|||
static String appConfig = 'appConfig';
|
||||
static String isDarkModeOn = 'isDarkModeOn';
|
||||
static String isOnbordingShowing = 'isOnbordingShowing';
|
||||
static String users = 'users';
|
||||
|
||||
static String appSettings = 'appSettings';
|
||||
static String servicesState = 'servicesState';
|
||||
|
||||
static String key = 'key';
|
||||
static String sshEnckey = 'sshEngkey';
|
||||
|
||||
static String cloudFlareDomain = 'cloudFlareDomain';
|
||||
static String hetznerKey = 'hetznerKey';
|
||||
|
@ -58,4 +65,7 @@ class BNames {
|
|||
static String isLoading = 'isLoading';
|
||||
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
|
||||
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
|
||||
static String sshConfig = 'sshConfig';
|
||||
static String sshPrivateKey = "sshPrivateKey";
|
||||
static String sshPublicKey = "sshPublicKey";
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
|||
import 'package:selfprivacy/logic/models/hetzner_server_info.dart';
|
||||
import 'package:selfprivacy/logic/models/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:selfprivacy/utils/password_generator2.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
class HetznerApi extends ApiMap {
|
||||
bool hasLoger;
|
||||
|
@ -73,30 +73,28 @@ class HetznerApi extends ApiMap {
|
|||
required User rootUser,
|
||||
required String domainName,
|
||||
}) async {
|
||||
var dbPassword = getRandomString(40);
|
||||
|
||||
const chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
|
||||
var dbStorageName = getRandomString(6, chars);
|
||||
var client = await getClient();
|
||||
|
||||
Response dbCreateResponse = await client.post(
|
||||
'/volumes',
|
||||
data: {
|
||||
"size": 10,
|
||||
"name": dbStorageName,
|
||||
"name": StringGenerators.dbStorageName(),
|
||||
"labels": {"labelkey": "value"},
|
||||
"location": "fsn1",
|
||||
"automount": false,
|
||||
"format": "ext4"
|
||||
},
|
||||
);
|
||||
|
||||
var dbPassword = StringGenerators.dbPassword();
|
||||
var dbId = dbCreateResponse.data['volume']['id'];
|
||||
|
||||
/// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
|
||||
/// check the branch name, it could be "development" or "master".
|
||||
|
||||
var data = jsonDecode(
|
||||
'''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''',
|
||||
);
|
||||
'''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''');
|
||||
|
||||
Response serverCreateResponse = await client.post(
|
||||
'/servers',
|
||||
|
@ -192,4 +190,20 @@ class HetznerApi extends ApiMap {
|
|||
|
||||
return HetznerServerInfo.fromJson(response.data!['server']);
|
||||
}
|
||||
|
||||
Future<void> createReverseDns({
|
||||
required String ip4,
|
||||
required String domainName,
|
||||
}) async {
|
||||
var hetznerServer = getIt<ApiConfigModel>().hetznerServer;
|
||||
var client = await getClient();
|
||||
await client.post(
|
||||
'/servers/${hetznerServer!.id}/actions/change_dns_ptr',
|
||||
data: {
|
||||
"ip": ip4,
|
||||
"dns_ptr": domainName,
|
||||
},
|
||||
);
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'dart:io';
|
|||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
|
||||
import 'api_map.dart';
|
||||
|
||||
|
@ -31,7 +33,7 @@ class ServerApi extends ApiMap {
|
|||
|
||||
var client = await getClient();
|
||||
try {
|
||||
response = await client.get('/serviceStatus');
|
||||
response = await client.get('/services/status');
|
||||
res = response.statusCode == HttpStatus.ok;
|
||||
} catch (e) {
|
||||
res = false;
|
||||
|
@ -40,6 +42,92 @@ class ServerApi extends ApiMap {
|
|||
return res;
|
||||
}
|
||||
|
||||
Future<bool> createUser(User user) async {
|
||||
bool res;
|
||||
Response response;
|
||||
|
||||
var client = await getClient();
|
||||
try {
|
||||
response = await client.post(
|
||||
'/users/create',
|
||||
options: Options(
|
||||
headers: {
|
||||
"X-User": user.login,
|
||||
"X-Password":
|
||||
'\$6\$${user.hashPassword.salt}\$${user.hashPassword.hash}',
|
||||
},
|
||||
),
|
||||
);
|
||||
res = response.statusCode == HttpStatus.ok;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
res = false;
|
||||
}
|
||||
|
||||
close(client);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get rootAddress =>
|
||||
throw UnimplementedError('not used in with implementation');
|
||||
|
||||
Future<bool> apply() async {
|
||||
bool res;
|
||||
Response response;
|
||||
|
||||
var client = await getClient();
|
||||
try {
|
||||
response = await client.get(
|
||||
'/system/configuration/apply',
|
||||
);
|
||||
|
||||
res = response.statusCode == HttpStatus.ok;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
res = false;
|
||||
}
|
||||
|
||||
close(client);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> switchService(ServiceTypes type, bool needToTurnOn) async {
|
||||
var client = await getClient();
|
||||
client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}');
|
||||
client.close();
|
||||
}
|
||||
|
||||
Future<void> sendSsh(String ssh) async {
|
||||
var client = await getClient();
|
||||
client.post(
|
||||
'/services/ssh/enable',
|
||||
data: {"public_key": ssh},
|
||||
);
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
extension UrlServerExt on ServiceTypes {
|
||||
String get url {
|
||||
switch (this) {
|
||||
// case ServiceTypes.mail:
|
||||
// return ''; // cannot be swithch off
|
||||
// case ServiceTypes.messenger:
|
||||
// return ''; // external service
|
||||
// case ServiceTypes.video:
|
||||
// return ''; // jeetsu meet not working
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'bitwarden';
|
||||
case ServiceTypes.cloud:
|
||||
return 'nextcloud';
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'pleroma';
|
||||
case ServiceTypes.git:
|
||||
return 'gitea';
|
||||
case ServiceTypes.vpn:
|
||||
return 'ocserv';
|
||||
default:
|
||||
throw Exception('wrong state');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
|
||||
enum InitializingSteps {
|
||||
setHeznerKey,
|
||||
setCloudFlareKey,
|
||||
|
@ -10,3 +15,80 @@ enum InitializingSteps {
|
|||
}
|
||||
enum Period { hour, day, month }
|
||||
|
||||
enum ServiceTypes {
|
||||
mail,
|
||||
messenger,
|
||||
passwordManager,
|
||||
video,
|
||||
cloud,
|
||||
socialNetwork,
|
||||
git,
|
||||
vpn,
|
||||
}
|
||||
|
||||
extension ServiceTypesExt on ServiceTypes {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return 'services.mail.title'.tr();
|
||||
case ServiceTypes.messenger:
|
||||
return 'services.messenger.title'.tr();
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'services.password_manager.title'.tr();
|
||||
case ServiceTypes.video:
|
||||
return 'services.video.title'.tr();
|
||||
case ServiceTypes.cloud:
|
||||
return 'services.cloud.title'.tr();
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'services.social_network.title'.tr();
|
||||
case ServiceTypes.git:
|
||||
return 'services.git.title'.tr();
|
||||
case ServiceTypes.vpn:
|
||||
return 'services.vpn.title'.tr();
|
||||
}
|
||||
}
|
||||
|
||||
String get subtitle {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return 'services.mail.subtitle'.tr();
|
||||
case ServiceTypes.messenger:
|
||||
return 'services.messenger.subtitle'.tr();
|
||||
case ServiceTypes.passwordManager:
|
||||
return 'services.password_manager.subtitle'.tr();
|
||||
case ServiceTypes.video:
|
||||
return 'services.video.subtitle'.tr();
|
||||
case ServiceTypes.cloud:
|
||||
return 'services.cloud.subtitle'.tr();
|
||||
case ServiceTypes.socialNetwork:
|
||||
return 'services.social_network.subtitle'.tr();
|
||||
case ServiceTypes.git:
|
||||
return 'services.git.subtitle'.tr();
|
||||
case ServiceTypes.vpn:
|
||||
return 'services.vpn.subtitle'.tr();
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case ServiceTypes.mail:
|
||||
return BrandIcons.envelope;
|
||||
case ServiceTypes.messenger:
|
||||
return BrandIcons.messanger;
|
||||
case ServiceTypes.passwordManager:
|
||||
return BrandIcons.key;
|
||||
case ServiceTypes.video:
|
||||
return BrandIcons.webcam;
|
||||
case ServiceTypes.cloud:
|
||||
return BrandIcons.upload;
|
||||
case ServiceTypes.socialNetwork:
|
||||
return BrandIcons.social;
|
||||
case ServiceTypes.git:
|
||||
return BrandIcons.git;
|
||||
case ServiceTypes.vpn:
|
||||
return Ionicons.shield_checkmark_outline;
|
||||
}
|
||||
}
|
||||
|
||||
String get txt => this.toString().split('.')[1];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ import 'dart:async';
|
|||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/get_it/ssh.dart';
|
||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
|
||||
|
||||
|
@ -43,9 +46,10 @@ part 'app_config_state.dart';
|
|||
/// c. if server is okay set that fully checked
|
||||
|
||||
class AppConfigCubit extends Cubit<AppConfigState> {
|
||||
AppConfigCubit() : super(InitialAppConfigState());
|
||||
AppConfigCubit(this.servicesCubit) : super(InitialAppConfigState());
|
||||
|
||||
final repository = AppConfigRepository();
|
||||
final ServicesCubit servicesCubit;
|
||||
|
||||
Future<void> load() async {
|
||||
var state = await repository.load();
|
||||
|
@ -232,6 +236,7 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
|||
|
||||
if (isServerWorking) {
|
||||
await repository.saveHasFinalChecked(true);
|
||||
servicesCubit.allOn();
|
||||
|
||||
emit(state.copyWith(
|
||||
hasFinalChecked: true,
|
||||
|
@ -259,14 +264,19 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
|||
|
||||
void clearAppConfig() {
|
||||
closeTimer();
|
||||
servicesCubit.allOff();
|
||||
|
||||
repository.clearAppConfig();
|
||||
emit(InitialAppConfigState());
|
||||
}
|
||||
|
||||
Future<void> serverDelete() async {
|
||||
closeTimer();
|
||||
servicesCubit.allOff();
|
||||
|
||||
if (state.hetznerServer != null) {
|
||||
await repository.deleteServer(state.cloudFlareDomain!);
|
||||
await getIt<SSHModel>().clear();
|
||||
}
|
||||
await repository.deleteRecords();
|
||||
emit(AppConfigState(
|
||||
|
@ -316,7 +326,7 @@ class AppConfigCubit extends Cubit<AppConfigState> {
|
|||
|
||||
void createServerAndSetDnsRecords() async {
|
||||
AppConfigState _stateCopy = state;
|
||||
var onSuccess = (serverDetails) async {
|
||||
var onSuccess = (HetznerServerDetails serverDetails) async {
|
||||
await repository.createDnsRecords(
|
||||
serverDetails.ip4,
|
||||
state.cloudFlareDomain!,
|
||||
|
|
|
@ -149,7 +149,7 @@ class AppConfigRepository {
|
|||
}
|
||||
|
||||
Future<void> createDnsRecords(
|
||||
String? ip4,
|
||||
String ip4,
|
||||
CloudFlareDomain cloudFlareDomain,
|
||||
) async {
|
||||
var cloudflareApi = CloudflareApi();
|
||||
|
@ -163,6 +163,11 @@ class AppConfigRepository {
|
|||
ip4: ip4,
|
||||
cloudFlareDomain: cloudFlareDomain,
|
||||
);
|
||||
|
||||
await HetznerApi().createReverseDns(
|
||||
ip4: ip4,
|
||||
domainName: cloudFlareDomain.domainName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> isHttpServerWorking() async {
|
||||
|
|
|
@ -85,7 +85,17 @@ class AppConfigState extends Equatable {
|
|||
bool get isServerCreated => hetznerServer != null;
|
||||
|
||||
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
|
||||
int get progress => _fulfilementList.where((el) => el!).length;
|
||||
int get progress => _fulfilementList.where((el) => el!).length ;
|
||||
|
||||
int get porgressBar {
|
||||
if (progress < 6) {
|
||||
return progress;
|
||||
} else if (progress < 10) {
|
||||
return 6;
|
||||
} else {
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
|
||||
List<bool?> get _fulfilementList {
|
||||
var res = [
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:selfprivacy/logic/api_maps/backblaze.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/backblaze_credential.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class BackblazeFormCubit extends FormCubit {
|
||||
BackblazeFormCubit(this.initializingCubit) {
|
||||
|
@ -10,7 +11,7 @@ class BackblazeFormCubit extends FormCubit {
|
|||
keyId = FieldCubit(
|
||||
initalValue: '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
//ValidationModel<String>(
|
||||
//(s) => regExp.hasMatch(s), 'invalid key format'),
|
||||
//LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64')
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class CloudFlareFormCubit extends FormCubit {
|
||||
CloudFlareFormCubit(this.initializingCubit) {
|
||||
|
@ -11,10 +12,11 @@ class CloudFlareFormCubit extends FormCubit {
|
|||
apiKey = FieldCubit(
|
||||
initalValue: '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>(
|
||||
(s) => regExp.hasMatch(s), 'invalid key format'),
|
||||
LegnthStringValidationWithLenghShowing(40, 'length is [] shoud be 40')
|
||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
||||
LegnthStringValidationWithLenghShowing(
|
||||
40, 'validations.length'.tr(args: ["40"]))
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class HetznerFormCubit extends FormCubit {
|
||||
HetznerFormCubit(this.initializingCubit) {
|
||||
|
@ -11,10 +12,10 @@ class HetznerFormCubit extends FormCubit {
|
|||
apiKey = FieldCubit(
|
||||
initalValue: '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>(
|
||||
(s) => regExp.hasMatch(s), 'invalid key format'),
|
||||
LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64')
|
||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
||||
LegnthStringValidationWithLenghShowing(64, 'validations.length'.tr(args: ["64"]))
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class RootUserFormCubit extends FormCubit {
|
||||
RootUserFormCubit(this.initializingCubit) {
|
||||
|
@ -12,18 +13,20 @@ class RootUserFormCubit extends FormCubit {
|
|||
userName = FieldCubit(
|
||||
initalValue: '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
ValidationModel<String>(
|
||||
(s) => userRegExp.hasMatch(s), 'invalid format'),
|
||||
(s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>(
|
||||
(s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()),
|
||||
],
|
||||
);
|
||||
|
||||
password = FieldCubit(
|
||||
initalValue: '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>(
|
||||
(s) => passwordRegExp.hasMatch(s), 'invalid format'),
|
||||
(s) => passwordRegExp.hasMatch(s), 'validations.invalid_format'.tr()),
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
class UserFormCubit extends FormCubit {
|
||||
UserFormCubit({
|
||||
required this.usersCubit,
|
||||
required this.jobsCubit,
|
||||
required List<User> users,
|
||||
User? user,
|
||||
}) {
|
||||
var isEdit = user != null;
|
||||
|
@ -18,18 +21,22 @@ class UserFormCubit extends FormCubit {
|
|||
login = FieldCubit(
|
||||
initalValue: isEdit ? user!.login : '',
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
ValidationModel(
|
||||
(login) => users.any((user) => user.login == login),
|
||||
'validations.user_alredy_exist'.tr(),
|
||||
),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>(
|
||||
(s) => userRegExp.hasMatch(s), 'invalid format'),
|
||||
(s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()),
|
||||
],
|
||||
);
|
||||
|
||||
password = FieldCubit(
|
||||
initalValue: isEdit ? user!.password : genPass(),
|
||||
initalValue: isEdit ? user!.password : StringGenerators.userPassword(),
|
||||
validations: [
|
||||
RequiredStringValidation('required'),
|
||||
ValidationModel<String>(
|
||||
(s) => passwordRegExp.hasMatch(s), 'invalid format'),
|
||||
RequiredStringValidation('validations.required'.tr()),
|
||||
ValidationModel<String>((s) => passwordRegExp.hasMatch(s),
|
||||
'validations.invalid_format'.tr()),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -42,15 +49,15 @@ class UserFormCubit extends FormCubit {
|
|||
login: login.state.value,
|
||||
password: password.state.value,
|
||||
);
|
||||
usersCubit.addUser(user);
|
||||
jobsCubit.addJob(CreateUserJob(user: user));
|
||||
}
|
||||
|
||||
late FieldCubit<String> login;
|
||||
late FieldCubit<String> password;
|
||||
|
||||
void genNewPassword() {
|
||||
password.externalSetValue(genPass());
|
||||
password.externalSetValue(StringGenerators.userPassword());
|
||||
}
|
||||
|
||||
late UsersCubit usersCubit;
|
||||
final JobsCubit jobsCubit;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'hetzner_metrics_cubit.dart';
|
|||
|
||||
class HetznerMetricsRepository {
|
||||
Future<HetznerMetricsLoaded> getMetrics(Period period) async {
|
||||
print(period);
|
||||
var end = DateTime.now();
|
||||
DateTime start;
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
import 'package:selfprivacy/logic/get_it/ssh.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
export 'package:provider/provider.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
part 'jobs_state.dart';
|
||||
|
||||
class JobsCubit extends Cubit<JobsState> {
|
||||
JobsCubit({
|
||||
required this.usersCubit,
|
||||
required this.servicesCubit,
|
||||
}) : super(JobsStateEmpty());
|
||||
|
||||
final api = ServerApi();
|
||||
final UsersCubit usersCubit;
|
||||
final ServicesCubit servicesCubit;
|
||||
|
||||
void addJob(Job job) {
|
||||
var newJobsList = <Job>[];
|
||||
if (state is JobsStateWithJobs) {
|
||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
|
||||
void removeJob(String id) {
|
||||
final newState = (state as JobsStateWithJobs).removeById(id);
|
||||
emit(newState);
|
||||
}
|
||||
|
||||
void createOrRemoveServiceToggleJob(ServiceToggleJob job) {
|
||||
var newJobsList = <Job>[];
|
||||
if (state is JobsStateWithJobs) {
|
||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
var needToRemoveJob =
|
||||
newJobsList.any((el) => el is ServiceToggleJob && el.type == job.type);
|
||||
if (needToRemoveJob) {
|
||||
var removingJob = newJobsList
|
||||
.firstWhere(((el) => el is ServiceToggleJob && el.type == job.type));
|
||||
removeJob(removingJob.id);
|
||||
} else {
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
}
|
||||
|
||||
void createShhJobIfNotExist(CreateSSHKeyJob job) {
|
||||
var newJobsList = <Job>[];
|
||||
if (state is JobsStateWithJobs) {
|
||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||
}
|
||||
var isExistInJobList = newJobsList.any((el) => el is CreateSSHKeyJob);
|
||||
if (!isExistInJobList) {
|
||||
newJobsList.add(job);
|
||||
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
|
||||
emit(JobsStateWithJobs(newJobsList));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> applyAll() async {
|
||||
if (state is JobsStateWithJobs) {
|
||||
var jobs = (state as JobsStateWithJobs).jobList;
|
||||
emit(JobsStateLoading());
|
||||
var newUsers = <User>[];
|
||||
for (var job in jobs) {
|
||||
if (job is CreateUserJob) {
|
||||
newUsers.add(job.user);
|
||||
await api.createUser(job.user);
|
||||
} else if (job is ServiceToggleJob) {
|
||||
await api.switchService(job.type, job.needToTurnOn);
|
||||
if (job.needToTurnOn) {
|
||||
servicesCubit.turnOnist([job.type]);
|
||||
} else {
|
||||
servicesCubit.turnOffList([job.type]);
|
||||
}
|
||||
}
|
||||
if (job is CreateSSHKeyJob) {
|
||||
await getIt<SSHModel>().generateKeys();
|
||||
api.sendSsh(getIt<SSHModel>().savedPubKey!);
|
||||
}
|
||||
}
|
||||
|
||||
usersCubit.addUsers(newUsers);
|
||||
await api.apply();
|
||||
|
||||
emit(JobsStateEmpty());
|
||||
|
||||
getIt<NavigationService>().navigator!.pop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
part of 'jobs_cubit.dart';
|
||||
|
||||
abstract class JobsState extends Equatable {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class JobsStateLoading extends JobsState {}
|
||||
|
||||
class JobsStateEmpty extends JobsState {}
|
||||
|
||||
class JobsStateWithJobs extends JobsState {
|
||||
JobsStateWithJobs(this.jobList);
|
||||
final List<Job> jobList;
|
||||
|
||||
JobsState removeById(String id) {
|
||||
var newJobsList = jobList.where((element) => element.id != id).toList();
|
||||
|
||||
if (newJobsList.isEmpty) {
|
||||
return JobsStateEmpty();
|
||||
}
|
||||
return JobsStateWithJobs(newJobsList);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => jobList;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
|
||||
part 'services_state.dart';
|
||||
|
||||
class ServicesCubit extends Cubit<ServicesState> {
|
||||
ServicesCubit() : super(ServicesState.allOff());
|
||||
|
||||
Box box = Hive.box(BNames.servicesState);
|
||||
|
||||
void load() {
|
||||
emit(
|
||||
ServicesState(
|
||||
isPasswordManagerEnable:
|
||||
box.get(ServiceTypes.passwordManager.txt, defaultValue: false),
|
||||
isCloudEnable: box.get(ServiceTypes.cloud.txt, defaultValue: false),
|
||||
isGitEnable: box.get(ServiceTypes.git.txt, defaultValue: false),
|
||||
isSocialNetworkEnable:
|
||||
box.get(ServiceTypes.socialNetwork.txt, defaultValue: false),
|
||||
isVpnEnable: box.get(ServiceTypes.vpn.txt, defaultValue: false),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void allOn() {
|
||||
box.put(ServiceTypes.passwordManager.txt, true);
|
||||
box.put(ServiceTypes.cloud.txt, true);
|
||||
box.put(ServiceTypes.git.txt, true);
|
||||
box.put(ServiceTypes.socialNetwork.txt, true);
|
||||
box.put(ServiceTypes.vpn.txt, true);
|
||||
|
||||
emit(ServicesState.allOn());
|
||||
}
|
||||
|
||||
void allOff() {
|
||||
box.put(ServiceTypes.passwordManager.txt, false);
|
||||
box.put(ServiceTypes.cloud.txt, false);
|
||||
box.put(ServiceTypes.git.txt, false);
|
||||
box.put(ServiceTypes.socialNetwork.txt, false);
|
||||
box.put(ServiceTypes.vpn.txt, false);
|
||||
|
||||
emit(ServicesState.allOff());
|
||||
}
|
||||
|
||||
void turnOffList(List<ServiceTypes> list) {
|
||||
for (final service in list) {
|
||||
box.put(service.txt, false);
|
||||
}
|
||||
|
||||
emit(state.disableList(list));
|
||||
}
|
||||
|
||||
void turnOnist(List<ServiceTypes> list) {
|
||||
for (final service in list) {
|
||||
box.put(service.txt, true);
|
||||
}
|
||||
|
||||
emit(state.enableList(list));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
part of 'services_cubit.dart';
|
||||
|
||||
class ServicesState extends Equatable {
|
||||
const ServicesState({
|
||||
required this.isPasswordManagerEnable,
|
||||
required this.isCloudEnable,
|
||||
required this.isGitEnable,
|
||||
required this.isSocialNetworkEnable,
|
||||
required this.isVpnEnable,
|
||||
});
|
||||
|
||||
final bool isPasswordManagerEnable;
|
||||
final bool isCloudEnable;
|
||||
final bool isGitEnable;
|
||||
final bool isSocialNetworkEnable;
|
||||
final bool isVpnEnable;
|
||||
|
||||
factory ServicesState.allOff() => ServicesState(
|
||||
isPasswordManagerEnable: false,
|
||||
isCloudEnable: false,
|
||||
isGitEnable: false,
|
||||
isSocialNetworkEnable: false,
|
||||
isVpnEnable: false,
|
||||
);
|
||||
factory ServicesState.allOn() => ServicesState(
|
||||
isPasswordManagerEnable: true,
|
||||
isCloudEnable: true,
|
||||
isGitEnable: true,
|
||||
isSocialNetworkEnable: true,
|
||||
isVpnEnable: true,
|
||||
);
|
||||
|
||||
ServicesState enableList(
|
||||
List<ServiceTypes> list,
|
||||
) =>
|
||||
ServicesState(
|
||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||
? true
|
||||
: isPasswordManagerEnable,
|
||||
isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable,
|
||||
isGitEnable:
|
||||
list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable,
|
||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
||||
? true
|
||||
: isPasswordManagerEnable,
|
||||
isVpnEnable:
|
||||
list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable,
|
||||
);
|
||||
|
||||
ServicesState disableList(
|
||||
List<ServiceTypes> list,
|
||||
) =>
|
||||
ServicesState(
|
||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||
? false
|
||||
: isPasswordManagerEnable,
|
||||
isCloudEnable:
|
||||
list.contains(ServiceTypes.cloud) ? false : isCloudEnable,
|
||||
isGitEnable:
|
||||
list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable,
|
||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
||||
? false
|
||||
: isPasswordManagerEnable,
|
||||
isVpnEnable:
|
||||
list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
isPasswordManagerEnable,
|
||||
isCloudEnable,
|
||||
isGitEnable,
|
||||
isSocialNetworkEnable,
|
||||
isVpnEnable
|
||||
];
|
||||
|
||||
bool isEnableByType(ServiceTypes type) {
|
||||
switch (type) {
|
||||
case ServiceTypes.passwordManager:
|
||||
return isPasswordManagerEnable;
|
||||
case ServiceTypes.cloud:
|
||||
return isCloudEnable;
|
||||
case ServiceTypes.socialNetwork:
|
||||
return isSocialNetworkEnable;
|
||||
case ServiceTypes.git:
|
||||
return isGitEnable;
|
||||
case ServiceTypes.vpn:
|
||||
return isVpnEnable;
|
||||
default:
|
||||
throw Exception('wrong state');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +1,36 @@
|
|||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
export 'package:provider/provider.dart';
|
||||
|
||||
part 'users_state.dart';
|
||||
|
||||
class UsersCubit extends Cubit<UsersState> {
|
||||
UsersCubit() : super(UsersState([]));
|
||||
UsersCubit() : super(UsersState(<User>[]));
|
||||
Box<User> box = Hive.box<User>(BNames.users);
|
||||
|
||||
void addUser(User user) {
|
||||
var users = [...state.users];
|
||||
users.add(user);
|
||||
|
||||
emit(UsersState(users));
|
||||
void load() async {
|
||||
var loadedUsers = box.values.toList();
|
||||
if (loadedUsers.isNotEmpty) {
|
||||
emit(UsersState(loadedUsers));
|
||||
}
|
||||
}
|
||||
|
||||
void remove(User? user) {
|
||||
void addUsers(List<User> users) async {
|
||||
var newUserList = <User>[...state.users, ...users];
|
||||
|
||||
await box.addAll(users);
|
||||
emit(UsersState(newUserList));
|
||||
}
|
||||
|
||||
void remove(User user) async {
|
||||
var users = [...state.users];
|
||||
var index = users.indexOf(user);
|
||||
users.remove(user);
|
||||
await box.deleteAt(index);
|
||||
|
||||
emit(UsersState(users));
|
||||
}
|
||||
}
|
||||
|
||||
// final initMockUsers = <User>[
|
||||
// User(login: 'Heartbreaking.Goose', password: genPass()),
|
||||
// User(login: 'Alma.lawson', password: genPass()),
|
||||
// User(login: 'Bee.gees', password: genPass()),
|
||||
// User(login: 'Bim.jennings', password: genPass()),
|
||||
// User(login: 'Debra.holt', password: genPass()),
|
||||
// User(login: 'Georgia.young', password: genPass()),
|
||||
// User(login: 'Kenzi.lawson', password: genPass()),
|
||||
// User(login: 'Le.jennings', password: genPass()),
|
||||
// User(login: 'Kirill.Zh', password: genPass()),
|
||||
// User(login: 'Tina.Bolton', password: genPass()),
|
||||
// User(login: 'Rebekah.Lynn', password: genPass()),
|
||||
// User(login: 'Aleena.Armstrong', password: genPass()),
|
||||
// User(login: 'Rosemary.Williams', password: genPass()),
|
||||
// User(login: 'Sullivan.Nixon', password: genPass()),
|
||||
// User(login: 'Aleena.Armstrong', password: genPass()),
|
||||
// ];
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
|
||||
class NavigationService {
|
||||
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
|
||||
GlobalKey<ScaffoldMessengerState>();
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
NavigatorState? get navigator => navigatorKey.currentState;
|
||||
|
||||
void showPopUpDialog(AlertDialog dialog) {
|
||||
|
@ -13,4 +18,14 @@ class NavigationService {
|
|||
builder: (_) => dialog,
|
||||
);
|
||||
}
|
||||
|
||||
void showSnackBar(String text) {
|
||||
final state = scaffoldMessengerKey.currentState!;
|
||||
final snack = SnackBar(
|
||||
backgroundColor: BrandColors.black.withOpacity(0.8),
|
||||
content: Text(text, style: buttonTitleText),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
state.showSnackBar(snack);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:pointycastle/pointycastle.dart';
|
||||
import 'package:rsa_encrypt/rsa_encrypt.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:pointycastle/api.dart' as crypto;
|
||||
import 'package:ssh_key/ssh_key.dart' as ssh_key;
|
||||
|
||||
class SSHModel {
|
||||
Box _box = Hive.box(BNames.sshConfig);
|
||||
String? savedPrivateKey;
|
||||
String? savedPubKey;
|
||||
|
||||
Future<void> generateKeys() async {
|
||||
var helper = RsaKeyHelper();
|
||||
crypto.AsymmetricKeyPair pair =
|
||||
await helper.computeRSAKeyPair(helper.getSecureRandom());
|
||||
var privateKey = pair.privateKey as RSAPrivateKey;
|
||||
var publicKey = pair.publicKey as RSAPublicKey;
|
||||
|
||||
savedPrivateKey = helper.encodePrivateKeyToPemPKCS1(privateKey);
|
||||
savedPubKey = publicKey.encode(ssh_key.PubKeyEncoding.openSsh);
|
||||
|
||||
await _box.put(BNames.sshPrivateKey, savedPrivateKey);
|
||||
await _box.put(BNames.sshPublicKey, savedPubKey);
|
||||
}
|
||||
|
||||
void init() async {
|
||||
savedPrivateKey = _box.get(BNames.sshPrivateKey);
|
||||
savedPubKey = _box.get(BNames.sshPublicKey);
|
||||
}
|
||||
|
||||
bool get isSSHKeyGenerated => savedPrivateKey != null && savedPubKey != null;
|
||||
|
||||
Future<void> clear() async {
|
||||
savedPrivateKey = null;
|
||||
savedPubKey = null;
|
||||
await _box.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
import 'user.dart';
|
||||
|
||||
@immutable
|
||||
class Job extends Equatable {
|
||||
Job({
|
||||
String? id,
|
||||
required this.title,
|
||||
}) : id = id ?? StringGenerators.simpleId();
|
||||
|
||||
final String title;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
||||
|
||||
class CreateUserJob extends Job {
|
||||
CreateUserJob({
|
||||
required this.user,
|
||||
}) : super(title: '${"jobs.createUser".tr()} ${user.login}');
|
||||
|
||||
final User user;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title, user];
|
||||
}
|
||||
|
||||
class ServiceToggleJob extends Job {
|
||||
ServiceToggleJob({
|
||||
required this.type,
|
||||
required this.needToTurnOn,
|
||||
}) : super(
|
||||
title:
|
||||
'${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}');
|
||||
|
||||
final ServiceTypes type;
|
||||
final bool needToTurnOn;
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title, type, needToTurnOn];
|
||||
}
|
||||
|
||||
class CreateSSHKeyJob extends Job {
|
||||
CreateSSHKeyJob() : super(title: '${"more.create_ssh_key".tr()}');
|
||||
|
||||
@override
|
||||
List<Object> get props => [id, title];
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:crypt/crypt.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/utils/color_utils.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/utils/password_generator.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
|
@ -25,7 +26,10 @@ class User extends Equatable {
|
|||
|
||||
Color get color => stringToColor(login);
|
||||
|
||||
Crypt get hashPassword => Crypt.sha512(password);
|
||||
Crypt get hashPassword => Crypt.sha512(
|
||||
password,
|
||||
salt: StringGenerators.passwordSalt(),
|
||||
);
|
||||
|
||||
String toString() {
|
||||
return login;
|
||||
|
|
|
@ -16,47 +16,50 @@ import 'config/localization.dart';
|
|||
import 'logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await HiveConfig.init();
|
||||
Bloc.observer = SimpleBlocObserver();
|
||||
Wakelock.enable();
|
||||
await getItSetup();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
runApp(
|
||||
Localization(
|
||||
child: BlocAndProviderConfig(
|
||||
child: MyApp(),
|
||||
),
|
||||
),
|
||||
);
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AppSettingsState appSettings = context.watch<AppSettingsCubit>().state;
|
||||
return Localization(
|
||||
child: BlocAndProviderConfig(
|
||||
child: Builder(builder: (context) {
|
||||
var appSettings = context.watch<AppSettingsCubit>().state;
|
||||
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
|
||||
child: MaterialApp(
|
||||
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'SelfPrivacy',
|
||||
theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme,
|
||||
home: appSettings.isOnbordingShowing
|
||||
? OnboardingPage(nextPage: InitializingPage())
|
||||
: RootPage(),
|
||||
builder: (BuildContext context, Widget? widget) {
|
||||
Widget error = Text('...rendering error...');
|
||||
if (widget is Scaffold || widget is Navigator)
|
||||
error = Scaffold(body: Center(child: error));
|
||||
ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error;
|
||||
return widget!;
|
||||
},
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
|
||||
child: MaterialApp(
|
||||
scaffoldMessengerKey:
|
||||
getIt.get<NavigationService>().scaffoldMessengerKey,
|
||||
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'SelfPrivacy',
|
||||
theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme,
|
||||
home: appSettings.isOnbordingShowing
|
||||
? OnboardingPage(nextPage: InitializingPage())
|
||||
: RootPage(),
|
||||
builder: (BuildContext context, Widget? widget) {
|
||||
Widget error = Text('...rendering error...');
|
||||
if (widget is Scaffold || widget is Navigator)
|
||||
error = Scaffold(body: Center(child: error));
|
||||
ErrorWidget.builder =
|
||||
(FlutterErrorDetails errorDetails) => error;
|
||||
return widget!;
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
|
||||
class BrandBottomSheet extends StatelessWidget {
|
||||
const BrandBottomSheet({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.isExpended = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget child;
|
||||
final bool isExpended;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var mainHeight = MediaQuery.of(context).size.height -
|
||||
MediaQuery.of(context).padding.top -
|
||||
100;
|
||||
late Widget innerWidget;
|
||||
if (isExpended) {
|
||||
innerWidget = Scaffold(
|
||||
body: child,
|
||||
);
|
||||
} else {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
|
||||
innerWidget = Material(
|
||||
color: themeData.scaffoldBackgroundColor,
|
||||
child: IntrinsicHeight(child: child),
|
||||
);
|
||||
}
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: mainHeight + 4 + 6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
height: 4,
|
||||
width: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
color: BrandColors.gray4,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 6),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: mainHeight),
|
||||
child: innerWidget,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,14 +10,14 @@ class BrandButton {
|
|||
static rised({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
String? title,
|
||||
String? text,
|
||||
Widget? child,
|
||||
}) {
|
||||
assert(title == null || child == null, 'required title or child');
|
||||
assert(title != null || child != null, 'required title or child');
|
||||
assert(text == null || child == null, 'required title or child');
|
||||
assert(text != null || child != null, 'required title or child');
|
||||
return _RisedButton(
|
||||
key: key,
|
||||
title: title,
|
||||
title: text,
|
||||
onPressed: onPressed,
|
||||
child: child,
|
||||
);
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/utils/extensions/elevation_extension.dart';
|
||||
|
||||
class BrandCard extends StatelessWidget {
|
||||
const BrandCard({
|
||||
Key? key,
|
||||
this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.black
|
||||
: BrandColors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
).ev8,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 15,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
|
||||
class BrandCards {
|
||||
static Widget big({required Widget child}) => _BrandCard(
|
||||
child: child,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 15,
|
||||
),
|
||||
shadow: bigShadow,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
);
|
||||
static Widget small({required Widget child}) => _BrandCard(
|
||||
child: child,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
vertical: 10,
|
||||
),
|
||||
shadow: bigShadow,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
);
|
||||
}
|
||||
|
||||
class _BrandCard extends StatelessWidget {
|
||||
const _BrandCard({
|
||||
Key? key,
|
||||
required this.child,
|
||||
required this.padding,
|
||||
required this.shadow,
|
||||
required this.borderRadius,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget child;
|
||||
final EdgeInsets padding;
|
||||
final List<BoxShadow> shadow;
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.black
|
||||
: BrandColors.white,
|
||||
borderRadius: borderRadius,
|
||||
boxShadow: shadow,
|
||||
),
|
||||
padding: padding,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final bigShadow = [
|
||||
BoxShadow(
|
||||
offset: Offset(0, 4),
|
||||
blurRadius: 8,
|
||||
color: Colors.black.withOpacity(.08),
|
||||
)
|
||||
];
|
|
@ -1,16 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/pre_styled_buttons.dart';
|
||||
|
||||
class BrandHeader extends StatelessWidget {
|
||||
const BrandHeader({
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.hasBackButton = false,
|
||||
this.hasFlashButton = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
final bool hasBackButton;
|
||||
final bool hasFlashButton;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -31,6 +34,8 @@ class BrandHeader extends StatelessWidget {
|
|||
SizedBox(width: 10),
|
||||
],
|
||||
BrandText.h4(title),
|
||||
Spacer(),
|
||||
if (hasFlashButton) PreStyledButtons.flash(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class BrandLoader {
|
||||
static horizontal() => _HorizontalLoader();
|
||||
}
|
||||
|
||||
class _HorizontalLoader extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('basis.wait'.tr()),
|
||||
SizedBox(height: 10),
|
||||
LinearProgressIndicator(minHeight: 3),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
|
|||
),
|
||||
);
|
||||
return Markdown(
|
||||
shrinkWrap: true,
|
||||
styleSheet: markdown,
|
||||
onTapLink: (String text, String? href, String title) {
|
||||
if (href != null) {
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
var navigatorKey = GlobalKey<NavigatorState>();
|
||||
// var navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
class BrandModalSheet extends StatelessWidget {
|
||||
const BrandModalSheet({
|
||||
Key? key,
|
||||
this.child,
|
||||
}) : super(key: key);
|
||||
// class BrandModalSheet extends StatelessWidget {
|
||||
// const BrandModalSheet({
|
||||
// Key? key,
|
||||
// this.child,
|
||||
// }) : super(key: key);
|
||||
|
||||
final Widget? child;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DraggableScrollableSheet(
|
||||
minChildSize: 0.95,
|
||||
initialChildSize: 1,
|
||||
maxChildSize: 1,
|
||||
builder: (context, scrollController) {
|
||||
return SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
physics: ClampingScrollPhysics(),
|
||||
child: Container(
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 132, bottom: 6),
|
||||
child: Container(
|
||||
height: 4,
|
||||
width: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
color: Color(0xFFE3E3E3).withOpacity(0.65),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: MediaQuery.of(context).size.height - 132,
|
||||
maxHeight: MediaQuery.of(context).size.height - 132,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(top: Radius.circular(20)),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
width: double.infinity,
|
||||
child: child),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
// final Widget? child;
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return DraggableScrollableSheet(
|
||||
// minChildSize: 1,
|
||||
// initialChildSize: 1,
|
||||
// maxChildSize: 1,
|
||||
// builder: (context, scrollController) {
|
||||
// return SingleChildScrollView(
|
||||
// controller: scrollController,
|
||||
// physics: ClampingScrollPhysics(),
|
||||
// child: Container(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () => Navigator.of(context).pop(),
|
||||
// behavior: HitTestBehavior.opaque,
|
||||
// child: Container(
|
||||
// width: double.infinity,
|
||||
// child: Center(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(top: 132, bottom: 6),
|
||||
// child: Container(
|
||||
// height: 4,
|
||||
// width: 30,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(2),
|
||||
// color: Color(0xFFE3E3E3).withOpacity(0.65),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Container(
|
||||
// constraints: BoxConstraints(
|
||||
// minHeight: MediaQuery.of(context).size.height - 132,
|
||||
// maxHeight: MediaQuery.of(context).size.height - 132,
|
||||
// ),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius:
|
||||
// BorderRadius.vertical(top: Radius.circular(20)),
|
||||
// color: Theme.of(context).scaffoldBackgroundColor,
|
||||
// ),
|
||||
// width: double.infinity,
|
||||
// child: child),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
|
||||
class BrandSwitch extends StatelessWidget {
|
||||
const BrandSwitch({
|
||||
Key? key,
|
||||
required this.onChanged,
|
||||
required this.value,
|
||||
}) : super(key: key);
|
||||
|
||||
final ValueChanged<bool> onChanged;
|
||||
final bool value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Switch(
|
||||
activeColor: BrandColors.green1,
|
||||
activeTrackColor: BrandColors.green2,
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
export 'package:selfprivacy/utils/extensions/text_extensions.dart';
|
||||
|
||||
enum TextType {
|
||||
h1, // right now only at onboarding and opened providers
|
||||
|
@ -51,10 +52,16 @@ class BrandText extends StatelessWidget {
|
|||
type: TextType.onboardingTitle,
|
||||
style: style,
|
||||
);
|
||||
factory BrandText.h2(String? text, {TextStyle? style}) => BrandText(
|
||||
factory BrandText.h2(
|
||||
String? text, {
|
||||
TextStyle? style,
|
||||
TextAlign? textAlign,
|
||||
}) =>
|
||||
BrandText(
|
||||
text,
|
||||
type: TextType.h2,
|
||||
style: style,
|
||||
textAlign: textAlign,
|
||||
);
|
||||
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
||||
BrandText(
|
||||
|
@ -120,7 +127,6 @@ class BrandText extends StatelessWidget {
|
|||
Text build(BuildContext context) {
|
||||
TextStyle style;
|
||||
var isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
switch (type) {
|
||||
case TextType.h1:
|
||||
style = isDark
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.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/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
|
||||
class JobsContent extends StatelessWidget {
|
||||
const JobsContent({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<JobsCubit, JobsState>(
|
||||
builder: (context, state) {
|
||||
late final List<Widget> widgets;
|
||||
if (state is JobsStateEmpty) {
|
||||
widgets = [
|
||||
SizedBox(height: 80),
|
||||
Center(child: BrandText.body1('jobs.empty'.tr())),
|
||||
];
|
||||
} else if (state is JobsStateLoading) {
|
||||
widgets = [
|
||||
SizedBox(height: 80),
|
||||
BrandLoader.horizontal(),
|
||||
];
|
||||
} else if (state is JobsStateWithJobs) {
|
||||
widgets = [
|
||||
...state.jobList
|
||||
.map(
|
||||
(j) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BrandCards.small(
|
||||
child: Text(j.title),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: BrandColors.red1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
onPressed: () =>
|
||||
context.read<JobsCubit>().removeJob(j.id),
|
||||
child: Text('basis.remove'.tr()),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
SizedBox(height: 20),
|
||||
BrandButton.rised(
|
||||
onPressed: () => context.read<JobsCubit>().applyAll(),
|
||||
text: 'jobs.start'.tr(),
|
||||
),
|
||||
];
|
||||
}
|
||||
return ListView(
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
SizedBox(height: 15),
|
||||
Center(
|
||||
child: BrandText.h2(
|
||||
'jobs.title'.tr(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
...widgets
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/pre_styled_buttons.dart';
|
||||
|
||||
class OnePage extends StatelessWidget {
|
||||
const OnePage({
|
||||
|
@ -16,33 +16,31 @@ class OnePage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 51,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: BrandText.h4('basis.details'.tr()),
|
||||
),
|
||||
BrandDivider(),
|
||||
],
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: child,
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(boxShadow: kElevationToShadow[3]),
|
||||
height: kBottomNavigationBarHeight,
|
||||
child: Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 51,
|
||||
alignment: Alignment.center,
|
||||
child: PreStyledButtons.close(
|
||||
onPress: () => Navigator.of(context).pop()),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: BrandText.h4('basis.details'.tr()),
|
||||
),
|
||||
BrandDivider(),
|
||||
],
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: child,
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(boxShadow: kElevationToShadow[3]),
|
||||
height: kBottomNavigationBarHeight,
|
||||
child: Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
alignment: Alignment.center,
|
||||
child: PreStyledButtons.close(
|
||||
onPress: () => Navigator.of(context).pop()),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class PreStyledButtons {
|
||||
static Widget close({
|
||||
required VoidCallback onPress,
|
||||
}) =>
|
||||
_CloseButton(onPress: onPress);
|
||||
}
|
||||
part of 'pre_styled_buttons.dart';
|
||||
|
||||
class _CloseButton extends StatelessWidget {
|
||||
const _CloseButton({Key? key, required this.onPress}) : super(key: key);
|
|
@ -0,0 +1,86 @@
|
|||
part of 'pre_styled_buttons.dart';
|
||||
|
||||
class _BrandFlashButton extends StatefulWidget {
|
||||
_BrandFlashButton({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_BrandFlashButtonState createState() => _BrandFlashButtonState();
|
||||
}
|
||||
|
||||
class _BrandFlashButtonState extends State<_BrandFlashButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation _colorTween;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_animationController =
|
||||
AnimationController(vsync: this, duration: Duration(milliseconds: 800));
|
||||
_colorTween = ColorTween(
|
||||
begin: BrandColors.black,
|
||||
end: BrandColors.primary,
|
||||
).animate(_animationController);
|
||||
|
||||
super.initState();
|
||||
WidgetsBinding.instance!.addPostFrameCallback(_afterLayout);
|
||||
}
|
||||
|
||||
void _afterLayout(_) {
|
||||
if (Theme.of(context).brightness == Brightness.dark) {
|
||||
setState(() {
|
||||
_colorTween = ColorTween(
|
||||
begin: BrandColors.white,
|
||||
end: BrandColors.primary,
|
||||
).animate(_animationController);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool wasPrevStateIsEmpty = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<JobsCubit, JobsState>(
|
||||
listener: (context, state) {
|
||||
if (wasPrevStateIsEmpty && state is! JobsStateEmpty) {
|
||||
wasPrevStateIsEmpty = false;
|
||||
_animationController.forward();
|
||||
} else if (!wasPrevStateIsEmpty && state is JobsStateEmpty) {
|
||||
wasPrevStateIsEmpty = true;
|
||||
|
||||
_animationController.reverse();
|
||||
}
|
||||
},
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
showBrandBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: JobsContent(),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: AnimatedBuilder(
|
||||
animation: _colorTween,
|
||||
builder: (context, child) {
|
||||
var v = _animationController.value;
|
||||
var icon = v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
|
||||
return Transform.scale(
|
||||
scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
|
||||
child: Icon(
|
||||
icon,
|
||||
color: _colorTween.value,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
|
||||
part 'close.dart';
|
||||
part 'flash.dart';
|
||||
|
||||
class PreStyledButtons {
|
||||
static Widget close({
|
||||
required VoidCallback onPress,
|
||||
}) =>
|
||||
_CloseButton(onPress: onPress);
|
||||
|
||||
static Widget flash() => _BrandFlashButton();
|
||||
}
|
|
@ -53,9 +53,9 @@ class _ProgressBarState extends State<ProgressBar> {
|
|||
width: 10,
|
||||
),
|
||||
);
|
||||
even.add(
|
||||
odd.add(
|
||||
SizedBox(
|
||||
width: 10,
|
||||
width: 20,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
Future<T?> showBrandBottomSheet<T>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
}) =>
|
||||
showCupertinoModalBottomSheet<T>(
|
||||
builder: builder,
|
||||
barrierColor: Colors.black45,
|
||||
context: context,
|
||||
shadow: BoxShadow(color: Colors.transparent),
|
||||
backgroundColor: Colors.transparent,
|
||||
);
|
|
@ -10,10 +10,10 @@ import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.da
|
|||
import 'package:selfprivacy/logic/cubit/forms/initializing/root_user_form_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/ui/components/brand_bottom_sheet/brand_bottom_sheet.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_cards/brand_cards.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_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
|
||||
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
|
||||
|
@ -36,7 +36,7 @@ class InitializingPage extends StatelessWidget {
|
|||
() => _stepCheck(cubit),
|
||||
() => _stepCheck(cubit),
|
||||
() => _stepCheck(cubit),
|
||||
() => Container(child: Text('Everythigng is initialized'))
|
||||
() => Container(child: Center(child: Text('initializing.finish'.tr())))
|
||||
][cubit.state.progress]();
|
||||
return BlocListener<AppConfigCubit, AppConfigState>(
|
||||
listener: (context, state) {
|
||||
|
@ -50,7 +50,7 @@ class InitializingPage extends StatelessWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: brandPagePadding2.copyWith(top: 10, bottom: 10),
|
||||
padding: paddingH15V0.copyWith(top: 10, bottom: 10),
|
||||
child: ProgressBar(
|
||||
steps: [
|
||||
'Hetzner',
|
||||
|
@ -59,12 +59,9 @@ class InitializingPage extends StatelessWidget {
|
|||
'Domain',
|
||||
'User',
|
||||
'Server',
|
||||
' ✅',
|
||||
' ✅',
|
||||
' ✅',
|
||||
' ✅',
|
||||
'✅ Check',
|
||||
],
|
||||
activeIndex: cubit.state.progress,
|
||||
activeIndex: cubit.state.porgressBar,
|
||||
),
|
||||
),
|
||||
_addCard(
|
||||
|
@ -132,7 +129,7 @@ class InitializingPage extends StatelessWidget {
|
|||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<HetznerFormCubit>().trySubmit(),
|
||||
title: 'basis.connect'.tr(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
|
@ -187,7 +184,7 @@ class InitializingPage extends StatelessWidget {
|
|||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<CloudFlareFormCubit>().trySubmit(),
|
||||
title: 'basis.connect'.tr(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
|
@ -238,7 +235,7 @@ class InitializingPage extends StatelessWidget {
|
|||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
||||
title: 'basis.connect'.tr(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
|
@ -330,7 +327,7 @@ class InitializingPage extends StatelessWidget {
|
|||
SizedBox(height: 30),
|
||||
BrandButton.rised(
|
||||
onPressed: () => context.read<DomainSetupCubit>().saveDomain(),
|
||||
title: 'initializing.10'.tr(),
|
||||
text: 'initializing.10'.tr(),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 10),
|
||||
|
@ -400,7 +397,7 @@ class InitializingPage extends StatelessWidget {
|
|||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<RootUserFormCubit>().trySubmit(),
|
||||
title: 'basis.connect'.tr(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
|
@ -428,7 +425,7 @@ class InitializingPage extends StatelessWidget {
|
|||
onPressed: isLoading
|
||||
? null
|
||||
: () => appConfigCubit.createServerAndSetDnsRecords(),
|
||||
title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(),
|
||||
text: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(),
|
||||
),
|
||||
Spacer(flex: 2),
|
||||
BrandButton.text(
|
||||
|
@ -443,21 +440,29 @@ class InitializingPage extends StatelessWidget {
|
|||
Widget _stepCheck(AppConfigCubit appConfigCubit) {
|
||||
assert(appConfigCubit.state is TimerState, 'wronge state');
|
||||
var state = appConfigCubit.state as TimerState;
|
||||
|
||||
late int doneCount;
|
||||
late String? text;
|
||||
if (state.isServerResetedSecondTime) {
|
||||
text = 'initializing.13'.tr();
|
||||
doneCount = 3;
|
||||
} else if (state.isServerResetedFirstTime) {
|
||||
text = 'initializing.21'.tr();
|
||||
doneCount = 2;
|
||||
} else if (state.isServerStarted) {
|
||||
text = 'initializing.14'.tr();
|
||||
doneCount = 1;
|
||||
} else if (state.isServerCreated) {
|
||||
text = 'initializing.15'.tr();
|
||||
doneCount = 0;
|
||||
}
|
||||
return Builder(builder: (context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 15),
|
||||
BrandText.h4(
|
||||
'initializing.checks'.tr(args: [doneCount.toString(), "4"]),
|
||||
),
|
||||
Spacer(flex: 2),
|
||||
SizedBox(height: 10),
|
||||
BrandText.body2(text),
|
||||
|
@ -488,8 +493,8 @@ class InitializingPage extends StatelessWidget {
|
|||
Widget _addCard(Widget child) {
|
||||
return Container(
|
||||
height: 450,
|
||||
padding: brandPagePadding2,
|
||||
child: BrandCard(child: child),
|
||||
padding: paddingH15V0,
|
||||
child: BrandCards.big(child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -501,12 +506,14 @@ class _HowHetzner extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BrandModalSheet(
|
||||
return BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: Padding(
|
||||
padding: brandPagePadding2.copyWith(top: 25),
|
||||
child: BrandMarkdown(
|
||||
fileName: 'how_hetzner',
|
||||
)),
|
||||
padding: paddingH15V0,
|
||||
child: BrandMarkdown(
|
||||
fileName: 'how_hetzner',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
|||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
@ -32,7 +33,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
BrandDivider(),
|
||||
Container(
|
||||
|
@ -52,9 +53,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Switch(
|
||||
activeColor: BrandColors.green1,
|
||||
activeTrackColor: BrandColors.green2,
|
||||
BrandSwitch(
|
||||
value: Theme.of(context).brightness == Brightness.dark,
|
||||
onChanged: (value) => context
|
||||
.read<AppSettingsCubit>()
|
||||
|
@ -120,69 +119,72 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
|
||||
)),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _TextColumn(
|
||||
title: 'more.settings.5'.tr(),
|
||||
value: 'more.settings.6'.tr(),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: BrandColors.red1,
|
||||
),
|
||||
child: Text(
|
||||
'basis.delete'.tr(),
|
||||
style: TextStyle(
|
||||
color: BrandColors.white,
|
||||
fontWeight: NamedFontWeight.demiBold,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BrandAlert(
|
||||
title: 'modals.3'.tr(),
|
||||
contentText: 'modals.6'.tr(),
|
||||
acitons: [
|
||||
ActionButton(
|
||||
text: 'modals.7'.tr(),
|
||||
isRed: true,
|
||||
onPressed: () async {
|
||||
await context
|
||||
.read<AppConfigCubit>()
|
||||
.serverDelete();
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
ActionButton(
|
||||
text: 'basis.cancel'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
// deleteServer(context)
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget deleteServer(BuildContext context) {
|
||||
// todo: need to check
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
|
||||
)),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _TextColumn(
|
||||
title: 'more.settings.5'.tr(),
|
||||
value: 'more.settings.6'.tr(),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: BrandColors.red1,
|
||||
),
|
||||
child: Text(
|
||||
'basis.delete'.tr(),
|
||||
style: TextStyle(
|
||||
color: BrandColors.white,
|
||||
fontWeight: NamedFontWeight.demiBold,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BrandAlert(
|
||||
title: 'modals.3'.tr(),
|
||||
contentText: 'modals.6'.tr(),
|
||||
acitons: [
|
||||
ActionButton(
|
||||
text: 'modals.7'.tr(),
|
||||
isRed: true,
|
||||
onPressed: () async {
|
||||
await context.read<AppConfigCubit>().serverDelete();
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
ActionButton(
|
||||
text: 'basis.cancel'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TextColumn extends StatelessWidget {
|
||||
|
|
|
@ -18,7 +18,7 @@ class InfoPage extends StatelessWidget {
|
|||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
BrandDivider(),
|
||||
SizedBox(height: 10),
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/get_it/ssh.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
|
||||
import 'package:selfprivacy/ui/pages/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||
import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import 'about/about.dart';
|
||||
import 'app_settings/app_setting.dart';
|
||||
|
@ -21,15 +35,21 @@ class MorePage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var jobsCubit = context.watch<JobsCubit>();
|
||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: BrandHeader(title: 'basis.more'.tr()),
|
||||
child: BrandHeader(
|
||||
title: 'basis.more'.tr(),
|
||||
hasFlashButton: true,
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
child: Column(
|
||||
children: [
|
||||
BrandDivider(),
|
||||
|
@ -63,6 +83,73 @@ class MorePage extends StatelessWidget {
|
|||
iconData: BrandIcons.terminal,
|
||||
goTo: Console(),
|
||||
),
|
||||
_MoreMenuTapItem(
|
||||
title: 'more.create_ssh_key'.tr(),
|
||||
iconData: Ionicons.key_outline,
|
||||
onTap: isReady
|
||||
? () {
|
||||
if (getIt<SSHModel>().isSSHKeyGenerated) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return _SSHExitsDetails(
|
||||
onShareTap: () {
|
||||
Share.share(
|
||||
getIt<SSHModel>().savedPrivateKey!);
|
||||
},
|
||||
onDeleteTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return BrandAlert(
|
||||
title: 'modals.3'.tr(),
|
||||
contentText:
|
||||
'more.delete_ssh_text'.tr(),
|
||||
acitons: [
|
||||
ActionButton(
|
||||
text: 'more.yes_delete'.tr(),
|
||||
isRed: true,
|
||||
onPressed: () {
|
||||
getIt<SSHModel>().clear();
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
ActionButton(
|
||||
text: 'basis.cancel'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onCopyTap: () {
|
||||
Clipboard.setData(ClipboardData(
|
||||
text: getIt<SSHModel>()
|
||||
.savedPrivateKey!));
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar('more.copied_ssh'.tr());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return _MoreDetails(
|
||||
title: 'more.create_ssh_key'.tr(),
|
||||
icon: Ionicons.key_outline,
|
||||
onTap: () {
|
||||
jobsCubit.createShhJobIfNotExist(
|
||||
CreateSSHKeyJob());
|
||||
},
|
||||
text: 'more.generate_key_text'.tr(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -72,6 +159,150 @@ class MorePage extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _SSHExitsDetails extends StatelessWidget {
|
||||
const _SSHExitsDetails({
|
||||
Key? key,
|
||||
required this.onDeleteTap,
|
||||
required this.onShareTap,
|
||||
required this.onCopyTap,
|
||||
}) : super(key: key);
|
||||
final Function onDeleteTap;
|
||||
final Function onShareTap;
|
||||
final Function onCopyTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var textStyle = body1Style.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: BrandColors.black);
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
width: 350,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Padding(
|
||||
padding: paddingH15V30,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
'more.ssh_key_exist_text'.tr(),
|
||||
style: textStyle,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
child: BrandButton.text(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onShareTap();
|
||||
},
|
||||
title: 'more.share'.tr(),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: BrandButton.text(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onDeleteTap();
|
||||
},
|
||||
title: 'basis.delete'.tr(),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: BrandButton.text(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onCopyTap();
|
||||
},
|
||||
title: 'more.copy_buffer'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoreDetails extends StatelessWidget {
|
||||
const _MoreDetails({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
required this.text,
|
||||
}) : super(key: key);
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final Function onTap;
|
||||
final String text;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var textStyle = body1Style.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: BrandColors.black);
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
width: 350,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: paddingH15V30,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: StateType.stable,
|
||||
child: Icon(icon, size: 40, color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(title),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
text,
|
||||
style: textStyle,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
Center(
|
||||
child: Container(
|
||||
child: BrandButton.rised(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onTap();
|
||||
},
|
||||
text: 'more.generate_key'.tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NavItem extends StatelessWidget {
|
||||
const _NavItem({
|
||||
Key? key,
|
||||
|
@ -88,29 +319,81 @@ class _NavItem extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(materialRoute(goTo)),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1.0,
|
||||
color: BrandColors.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
BrandText.body1(title),
|
||||
Spacer(),
|
||||
SizedBox(
|
||||
width: 56,
|
||||
child: Icon(
|
||||
iconData,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _MoreMenuItem(
|
||||
iconData: iconData,
|
||||
title: title,
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoreMenuTapItem extends StatelessWidget {
|
||||
const _MoreMenuTapItem({
|
||||
Key? key,
|
||||
required this.iconData,
|
||||
required this.onTap,
|
||||
required this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
final IconData iconData;
|
||||
final VoidCallback? onTap;
|
||||
final String title;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: _MoreMenuItem(
|
||||
isActive: onTap != null,
|
||||
iconData: iconData,
|
||||
title: title,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoreMenuItem extends StatelessWidget {
|
||||
const _MoreMenuItem({
|
||||
Key? key,
|
||||
required this.iconData,
|
||||
required this.title,
|
||||
required this.isActive,
|
||||
}) : super(key: key);
|
||||
|
||||
final IconData iconData;
|
||||
final String title;
|
||||
final bool isActive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1.0,
|
||||
color: BrandColors.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
BrandText.body1(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: isActive ? null : Colors.grey,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
SizedBox(
|
||||
width: 56,
|
||||
child: Icon(
|
||||
iconData,
|
||||
size: 20,
|
||||
color: isActive ? null : Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||
curve: Curves.easeIn,
|
||||
);
|
||||
},
|
||||
title: 'basis.next'.tr(),
|
||||
text: 'basis.next'.tr(),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
],
|
||||
|
@ -129,7 +129,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||
(route) => false,
|
||||
);
|
||||
},
|
||||
title: 'basis.got_it'.tr(),
|
||||
text: 'basis.got_it'.tr(),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
],
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/provider.dart';
|
||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
|
||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||
import 'package:selfprivacy/ui/components/one_page/one_page.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/server_details.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/slide_bottom.dart';
|
||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||
|
||||
var navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
@ -46,11 +45,14 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
.toList();
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: BrandHeader(title: 'providers.page_title'.tr()),
|
||||
child: BrandHeader(
|
||||
title: 'providers.page_title'.tr(),
|
||||
hasFlashButton: true,
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
if (!isReady) ...[
|
||||
NotReadyCard(),
|
||||
|
@ -73,7 +75,7 @@ class _Card extends StatelessWidget {
|
|||
String? message;
|
||||
late String stableText;
|
||||
late VoidCallback onTap;
|
||||
|
||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
AppConfigState appConfig = context.watch<AppConfigCubit>().state;
|
||||
|
||||
var domainName =
|
||||
|
@ -83,24 +85,22 @@ class _Card extends StatelessWidget {
|
|||
case ProviderType.server:
|
||||
title = 'providers.server.card_title'.tr();
|
||||
stableText = 'providers.server.status'.tr();
|
||||
onTap = () => Navigator.of(context).push(
|
||||
SlideBottomRoute(
|
||||
OnePage(
|
||||
title: title,
|
||||
child: ServerDetails(),
|
||||
),
|
||||
onTap = () => showBrandBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: ServerDetails(),
|
||||
),
|
||||
);
|
||||
|
||||
break;
|
||||
case ProviderType.domain:
|
||||
title = 'providers.domain.card_title'.tr();
|
||||
message = domainName;
|
||||
stableText = 'providers.domain.status'.tr();
|
||||
|
||||
onTap = () => showModalBottomSheet<void>(
|
||||
onTap = () => showBrandBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return _ProviderDetails(
|
||||
provider: provider,
|
||||
|
@ -127,8 +127,8 @@ class _Card extends StatelessWidget {
|
|||
break;
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: BrandCard(
|
||||
onTap: isReady ? onTap : null,
|
||||
child: BrandCards.big(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -196,37 +196,31 @@ class _ProviderDetails extends StatelessWidget {
|
|||
];
|
||||
break;
|
||||
}
|
||||
return BrandModalSheet(
|
||||
child: Navigator(
|
||||
key: navigatorKey,
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (_) {
|
||||
return materialRoute(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 40),
|
||||
Padding(
|
||||
padding: brandPagePadding2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: provider.state,
|
||||
child:
|
||||
Icon(provider.icon, size: 40, color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandText.h1(title),
|
||||
SizedBox(height: 10),
|
||||
...children
|
||||
],
|
||||
return BrandBottomSheet(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 40),
|
||||
Padding(
|
||||
padding: paddingH15V0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: provider.state,
|
||||
child: Icon(provider.icon, size: 40, color: Colors.white),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
SizedBox(height: 10),
|
||||
BrandText.h1(title),
|
||||
SizedBox(height: 10),
|
||||
...children,
|
||||
SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class SettingsPage extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
BrandHeader(title: 'basis.settings'.tr(), hasBackButton: true),
|
||||
|
|
|
@ -51,7 +51,7 @@ class CpuChart extends StatelessWidget {
|
|||
interval: 20,
|
||||
rotateAngle: 90.0,
|
||||
showTitles: true,
|
||||
getTextStyles: (value) => const TextStyle(
|
||||
getTextStyles: (_, __) => const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.purple,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -60,7 +60,7 @@ class CpuChart extends StatelessWidget {
|
|||
return bottomTitle(value.toInt());
|
||||
}),
|
||||
leftTitles: SideTitles(
|
||||
getTextStyles: (value) => progressTextStyleLight.copyWith(
|
||||
getTextStyles: (_, __) => progressTextStyleLight.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.gray4
|
||||
: null,
|
||||
|
@ -82,7 +82,6 @@ class CpuChart extends StatelessWidget {
|
|||
double appliedInterval,
|
||||
double value,
|
||||
) {
|
||||
print(value);
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
|
|
|
@ -71,7 +71,7 @@ class NetworkChart extends StatelessWidget {
|
|||
interval: 20,
|
||||
rotateAngle: 90.0,
|
||||
showTitles: true,
|
||||
getTextStyles: (value) => const TextStyle(
|
||||
getTextStyles: (_, __) => const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.purple,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -87,7 +87,7 @@ class NetworkChart extends StatelessWidget {
|
|||
].reduce(max) *
|
||||
1.2 /
|
||||
10,
|
||||
getTextStyles: (value) => progressTextStyleLight.copyWith(
|
||||
getTextStyles: (_, __) => progressTextStyleLight.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? BrandColors.gray4
|
||||
: null,
|
||||
|
|
|
@ -56,41 +56,58 @@ class _ServerDetailsState extends State<ServerDetails>
|
|||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
var providerState = isReady ? StateType.stable : StateType.uninitialized;
|
||||
|
||||
return TabBarView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
controller: tabController,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: brandPagePadding2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_Header(
|
||||
providerState: providerState,
|
||||
tabController: tabController),
|
||||
BrandText.body1('providers.server.bottom_sheet.1'.tr()),
|
||||
SizedBox(height: 10),
|
||||
BlocProvider(
|
||||
create: (context) => HetznerMetricsCubit()..restart(),
|
||||
child: _Chart(),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
BlocProvider(
|
||||
create: (context) => ServerDetailsCubit()..check(),
|
||||
child: _TextDetails(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 51,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: BrandText.h4('basis.details'.tr()),
|
||||
),
|
||||
BrandDivider(),
|
||||
],
|
||||
),
|
||||
_ServerSettings(tabController: tabController),
|
||||
],
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: TabBarView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
controller: tabController,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
physics: ClampingScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: paddingH15V0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_Header(
|
||||
providerState: providerState,
|
||||
tabController: tabController),
|
||||
BrandText.body1('providers.server.bottom_sheet.1'.tr()),
|
||||
SizedBox(height: 10),
|
||||
BlocProvider(
|
||||
create: (context) => HetznerMetricsCubit()..restart(),
|
||||
child: _Chart(),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
BlocProvider(
|
||||
create: (context) => ServerDetailsCubit()..check(),
|
||||
child: _TextDetails(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_ServerSettings(tabController: tabController),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class _ServerSettings extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
|
|
|
@ -27,13 +27,13 @@ class _TextDetails extends StatelessWidget {
|
|||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Last check'),
|
||||
getRowTitle('Last check:'),
|
||||
getRowValue(formater.format(checkTime)),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Server Id'),
|
||||
getRowTitle('Server Id:'),
|
||||
getRowValue(data.id.toString()),
|
||||
],
|
||||
),
|
||||
|
@ -48,7 +48,7 @@ class _TextDetails extends StatelessWidget {
|
|||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('CPU'),
|
||||
getRowTitle('CPU:'),
|
||||
getRowValue(
|
||||
data.serverType.cores.toString(),
|
||||
),
|
||||
|
@ -56,7 +56,7 @@ class _TextDetails extends StatelessWidget {
|
|||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Memory'),
|
||||
getRowTitle('Memory:'),
|
||||
getRowValue(
|
||||
'${data.serverType.memory.toString()} GB',
|
||||
),
|
||||
|
@ -64,7 +64,7 @@ class _TextDetails extends StatelessWidget {
|
|||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Disk Local'),
|
||||
getRowTitle('Disk Local:'),
|
||||
getRowValue(
|
||||
'${data.serverType.disk.toString()} GB',
|
||||
),
|
||||
|
@ -100,7 +100,7 @@ class _TextDetails extends StatelessWidget {
|
|||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Country'),
|
||||
getRowTitle('Country:'),
|
||||
getRowValue(
|
||||
'${data.location.country}',
|
||||
),
|
||||
|
@ -108,13 +108,13 @@ class _TextDetails extends StatelessWidget {
|
|||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('City'),
|
||||
getRowTitle('City:'),
|
||||
getRowValue(data.location.city),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
getRowTitle('Description'),
|
||||
getRowTitle('Description:'),
|
||||
getRowValue(data.location.description),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/config/text_themes.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
|
||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||
|
@ -17,6 +23,14 @@ import 'package:url_launcher/url_launcher.dart';
|
|||
|
||||
import '../rootRoute.dart';
|
||||
|
||||
const switchableServices = [
|
||||
ServiceTypes.passwordManager,
|
||||
ServiceTypes.cloud,
|
||||
ServiceTypes.socialNetwork,
|
||||
ServiceTypes.git,
|
||||
ServiceTypes.vpn,
|
||||
];
|
||||
|
||||
class ServicesPage extends StatefulWidget {
|
||||
ServicesPage({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -31,11 +45,14 @@ class _ServicesPageState extends State<ServicesPage> {
|
|||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: BrandHeader(title: 'basis.services'.tr()),
|
||||
child: BrandHeader(
|
||||
title: 'basis.services'.tr(),
|
||||
hasFlashButton: true,
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
body: ListView(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
BrandText.body1('services.title'.tr()),
|
||||
SizedBox(height: 24),
|
||||
|
@ -60,78 +77,115 @@ class _Card extends StatelessWidget {
|
|||
final ServiceTypes serviceType;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String title;
|
||||
IconData iconData;
|
||||
String subtitle;
|
||||
|
||||
switch (serviceType) {
|
||||
case ServiceTypes.mail:
|
||||
iconData = BrandIcons.envelope;
|
||||
title = 'services.mail.title'.tr();
|
||||
subtitle = 'services.mail.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.messenger:
|
||||
iconData = BrandIcons.messanger;
|
||||
title = 'services.messenger.title'.tr();
|
||||
subtitle = 'services.messenger.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.passwordManager:
|
||||
iconData = BrandIcons.key;
|
||||
title = 'services.password_manager.title'.tr();
|
||||
subtitle = 'services.password_manager.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.video:
|
||||
iconData = BrandIcons.webcam;
|
||||
title = 'services.video.title'.tr();
|
||||
subtitle = 'services.video.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.cloud:
|
||||
iconData = BrandIcons.upload;
|
||||
title = 'services.cloud.title'.tr();
|
||||
subtitle = 'services.cloud.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.socialNetwork:
|
||||
iconData = BrandIcons.social;
|
||||
title = 'services.social_network.title'.tr();
|
||||
subtitle = 'services.social_network.subtitle'.tr();
|
||||
break;
|
||||
case ServiceTypes.git:
|
||||
iconData = BrandIcons.git;
|
||||
title = 'services.git.title'.tr();
|
||||
subtitle = 'services.git.subtitle'.tr();
|
||||
break;
|
||||
}
|
||||
|
||||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
var changeTab = context.read<ChangeTab>().onPress;
|
||||
|
||||
var serviceState = context.watch<ServicesCubit>().state;
|
||||
var jobsCubit = context.watch<JobsCubit>();
|
||||
var jobState = jobsCubit.state;
|
||||
|
||||
var switchebleService = switchableServices.contains(serviceType);
|
||||
var hasSwitchJob = switchebleService &&
|
||||
jobState is JobsStateWithJobs &&
|
||||
jobState.jobList
|
||||
.any((el) => el is ServiceToggleJob && el.type == serviceType);
|
||||
|
||||
var isSwithOn = isReady &&
|
||||
(!switchableServices.contains(serviceType) ||
|
||||
serviceState.isEnableByType(serviceType));
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => showDialog<void>(
|
||||
context: context,
|
||||
// isScrollControlled: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return _ServiceDetails(
|
||||
serviceType: serviceType,
|
||||
status: isReady ? StateType.stable : StateType.uninitialized,
|
||||
title: title,
|
||||
icon: iconData,
|
||||
changeTab: changeTab,
|
||||
);
|
||||
},
|
||||
),
|
||||
child: BrandCard(
|
||||
onTap: isSwithOn
|
||||
? () => showDialog<void>(
|
||||
context: context,
|
||||
// isScrollControlled: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return _ServiceDetails(
|
||||
serviceType: serviceType,
|
||||
status:
|
||||
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||
title: serviceType.title,
|
||||
icon: serviceType.icon,
|
||||
changeTab: changeTab,
|
||||
);
|
||||
},
|
||||
)
|
||||
: null,
|
||||
child: BrandCards.big(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status: isReady ? StateType.stable : StateType.uninitialized,
|
||||
child: Icon(iconData, size: 30, color: Colors.white),
|
||||
Row(
|
||||
children: [
|
||||
IconStatusMask(
|
||||
status:
|
||||
isSwithOn ? StateType.stable : StateType.uninitialized,
|
||||
child: Icon(serviceType.icon, size: 30, color: Colors.white),
|
||||
),
|
||||
if (isReady && switchebleService) ...[
|
||||
Spacer(),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
late bool isActive;
|
||||
if (hasSwitchJob) {
|
||||
isActive = ((jobState as JobsStateWithJobs)
|
||||
.jobList
|
||||
.firstWhere((el) =>
|
||||
el is ServiceToggleJob &&
|
||||
el.type == serviceType) as ServiceToggleJob)
|
||||
.needToTurnOn;
|
||||
} else {
|
||||
isActive = serviceState.isEnableByType(serviceType);
|
||||
}
|
||||
|
||||
return BrandSwitch(
|
||||
value: isActive,
|
||||
onChanged: (value) =>
|
||||
jobsCubit.createOrRemoveServiceToggleJob(
|
||||
ServiceToggleJob(
|
||||
type: serviceType,
|
||||
needToTurnOn: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(title),
|
||||
SizedBox(height: 10),
|
||||
BrandText.body2(subtitle),
|
||||
SizedBox(height: 10),
|
||||
ClipRect(
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
BrandText.h2(serviceType.title),
|
||||
SizedBox(height: 10),
|
||||
BrandText.body2(serviceType.subtitle),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
if (hasSwitchJob)
|
||||
Positioned(
|
||||
bottom: 30,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: 3,
|
||||
sigmaY: 2,
|
||||
),
|
||||
child: BrandText.h2(
|
||||
'jobs.runJobs'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -139,16 +193,6 @@ class _Card extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
enum ServiceTypes {
|
||||
mail,
|
||||
messenger,
|
||||
passwordManager,
|
||||
video,
|
||||
cloud,
|
||||
socialNetwork,
|
||||
git,
|
||||
}
|
||||
|
||||
class _ServiceDetails extends StatelessWidget {
|
||||
const _ServiceDetails({
|
||||
Key? key,
|
||||
|
@ -347,7 +391,12 @@ class _ServiceDetails extends StatelessWidget {
|
|||
],
|
||||
));
|
||||
break;
|
||||
case ServiceTypes.vpn:
|
||||
child = Text(
|
||||
'services.vpn.bottom_sheet.1'.tr(),
|
||||
);
|
||||
}
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
|
@ -359,7 +408,7 @@ class _ServiceDetails extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: brandPagePadding1,
|
||||
padding: paddingH15V30,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -376,7 +425,7 @@ class _ServiceDetails extends StatelessWidget {
|
|||
child: Container(
|
||||
child: BrandButton.rised(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
title: 'basis.close'.tr(),
|
||||
text: 'basis.close'.tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -407,252 +456,3 @@ class _ServiceDetails extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// class _ServiceDetails extends StatelessWidget {
|
||||
// const _ServiceDetails({
|
||||
// Key? key,
|
||||
// required this.serviceType,
|
||||
// required this.icon,
|
||||
// required this.status,
|
||||
// required this.title,
|
||||
// required this.changeTab,
|
||||
// }) : super(key: key);
|
||||
|
||||
// final ServiceTypes serviceType;
|
||||
// final IconData icon;
|
||||
// final StateType status;
|
||||
// final String title;
|
||||
// final ValueChanged<int> changeTab;
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// late Widget child;
|
||||
|
||||
// var config = context.watch<AppConfigCubit>().state;
|
||||
// var domainName = UiHelpers.getDomainName(config);
|
||||
|
||||
// var linksStyle = body1Style.copyWith(
|
||||
// fontSize: 15,
|
||||
// color: Theme.of(context).brightness == Brightness.dark
|
||||
// ? Colors.white
|
||||
// : BrandColors.black,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// decoration: TextDecoration.underline,
|
||||
// // height: 1.1,
|
||||
// );
|
||||
|
||||
// var textStyle = body1Style.copyWith(
|
||||
// color: Theme.of(context).brightness == Brightness.dark
|
||||
// ? Colors.white
|
||||
// : BrandColors.black,
|
||||
// );
|
||||
// switch (serviceType) {
|
||||
// case ServiceTypes.mail:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// child: Text(
|
||||
// 'services.mail.bottom_sheet.2'.tr(),
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// onTap: () {
|
||||
// Navigator.of(context).pop();
|
||||
// changeTab(2);
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.messenger:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.messenger.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// )
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.passwordManager:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.password_manager.bottom_sheet.1'
|
||||
// .tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://password.$domainName'),
|
||||
// child: Text(
|
||||
// 'password.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.video:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.video.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://meet.$domainName'),
|
||||
// child: Text(
|
||||
// 'meet.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.cloud:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://cloud.$domainName'),
|
||||
// child: Text(
|
||||
// 'cloud.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.socialNetwork:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.social_network.bottom_sheet.1'
|
||||
// .tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://social.$domainName'),
|
||||
// child: Text(
|
||||
// 'social.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// case ServiceTypes.git:
|
||||
// child = RichText(
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'services.git.bottom_sheet.1'.tr(args: [domainName]),
|
||||
// style: textStyle,
|
||||
// ),
|
||||
// WidgetSpan(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 0.8, left: 5),
|
||||
// child: GestureDetector(
|
||||
// onTap: () => _launchURL('https://git.$domainName'),
|
||||
// child: Text(
|
||||
// 'git.$domainName',
|
||||
// style: linksStyle,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ));
|
||||
// break;
|
||||
// }
|
||||
// return BrandModalSheet(
|
||||
// child: Navigator(
|
||||
// key: navigatorKey,
|
||||
// initialRoute: '/',
|
||||
// onGenerateRoute: (_) {
|
||||
// return materialRoute(
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: brandPagePadding1,
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// SizedBox(height: 13),
|
||||
// IconStatusMask(
|
||||
// status: status,
|
||||
// child: Icon(icon, size: 40, color: Colors.white),
|
||||
// ),
|
||||
// SizedBox(height: 10),
|
||||
// BrandText.h1(title),
|
||||
// SizedBox(height: 10),
|
||||
// child,
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void _launchURL(url) async {
|
||||
// var _possible = await canLaunch(url);
|
||||
|
||||
// if (_possible) {
|
||||
// try {
|
||||
// await launch(
|
||||
// url,
|
||||
// forceSafariVC: true,
|
||||
// enableJavaScript: true,
|
||||
// );
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
// } else {
|
||||
// throw 'Could not launch $url';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
|
|
@ -7,10 +7,26 @@ class _NewUser extends StatelessWidget {
|
|||
|
||||
var domainName = UiHelpers.getDomainName(config);
|
||||
|
||||
return BrandModalSheet(
|
||||
return BrandBottomSheet(
|
||||
child: BlocProvider(
|
||||
create: (context) =>
|
||||
UserFormCubit(usersCubit: context.read<UsersCubit>()),
|
||||
create: (context) {
|
||||
var jobCubit = context.read<JobsCubit>();
|
||||
var jobState = jobCubit.state;
|
||||
var users = <User>[];
|
||||
users.addAll(context.read<UsersCubit>().state.users);
|
||||
if (jobState is JobsStateWithJobs) {
|
||||
var jobs = jobState.jobList;
|
||||
jobs.forEach((job) {
|
||||
if (job is CreateUserJob) {
|
||||
users.add(job.user);
|
||||
}
|
||||
});
|
||||
}
|
||||
return UserFormCubit(
|
||||
jobsCubit: jobCubit,
|
||||
users: users,
|
||||
);
|
||||
},
|
||||
child: Builder(builder: (context) {
|
||||
var formCubitState = context.watch<UserFormCubit>().state;
|
||||
|
||||
|
@ -22,20 +38,24 @@ class _NewUser extends StatelessWidget {
|
|||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BrandHeader(
|
||||
title: 'users.new_user'.tr(),
|
||||
),
|
||||
SizedBox(width: 14),
|
||||
Padding(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<UserFormCubit>().login,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'users.login'.tr(),
|
||||
suffixText: '@$domainName',
|
||||
IntrinsicHeight(
|
||||
child: CubitFormTextField(
|
||||
formFieldCubit: context.read<UserFormCubit>().login,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'users.login'.tr(),
|
||||
suffixText: '@$domainName',
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
@ -62,7 +82,7 @@ class _NewUser extends StatelessWidget {
|
|||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<UserFormCubit>().trySubmit(),
|
||||
title: 'basis.create'.tr(),
|
||||
text: 'basis.create'.tr(),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
Text('users.new_user_info_note'.tr()),
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
part of 'users.dart';
|
||||
|
||||
class _User extends StatelessWidget {
|
||||
const _User({Key? key, this.user}) : super(key: key);
|
||||
const _User({Key? key, required this.user}) : super(key: key);
|
||||
|
||||
final User? user;
|
||||
final User user;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
showBrandBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return _UserDetails(user: user);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: brandPagePadding2,
|
||||
padding: paddingH15V0,
|
||||
height: 48,
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -26,12 +24,12 @@ class _User extends StatelessWidget {
|
|||
width: 17,
|
||||
height: 17,
|
||||
decoration: BoxDecoration(
|
||||
color: user!.color,
|
||||
color: user.color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
BrandText.h4(user!.login),
|
||||
BrandText.h4(user.login),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -3,10 +3,10 @@ part of 'users.dart';
|
|||
class _UserDetails extends StatelessWidget {
|
||||
const _UserDetails({
|
||||
Key? key,
|
||||
this.user,
|
||||
required this.user,
|
||||
}) : super(key: key);
|
||||
|
||||
final User? user;
|
||||
final User user;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -14,14 +14,16 @@ class _UserDetails extends StatelessWidget {
|
|||
|
||||
var domainName = UiHelpers.getDomainName(config);
|
||||
|
||||
return BrandModalSheet(
|
||||
return BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: user!.color,
|
||||
color: user.color,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
|
@ -114,7 +116,7 @@ class _UserDetails extends StatelessWidget {
|
|||
horizontal: 15,
|
||||
),
|
||||
child: BrandText.h1(
|
||||
user!.login,
|
||||
user.login,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
|
@ -123,7 +125,7 @@ class _UserDetails extends StatelessWidget {
|
|||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: brandPagePadding2.copyWith(bottom: 20),
|
||||
padding: paddingH15V0.copyWith(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -131,14 +133,14 @@ class _UserDetails extends StatelessWidget {
|
|||
Container(
|
||||
height: 40,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: BrandText.h4('${user!.login}@$domainName'),
|
||||
child: BrandText.h4('${user.login}@$domainName'),
|
||||
),
|
||||
SizedBox(height: 14),
|
||||
BrandText.small('basis.password'.tr()),
|
||||
Container(
|
||||
height: 40,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: BrandText.h4(user!.password),
|
||||
child: BrandText.h4(user.password),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
BrandDivider(),
|
||||
|
@ -146,7 +148,10 @@ class _UserDetails extends StatelessWidget {
|
|||
BrandButton.emptyWithIconText(
|
||||
title: 'users.send_regisration_data'.tr(),
|
||||
icon: Icon(BrandIcons.share),
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Share.share(
|
||||
'login: ${user.login}, password: ${user.password}');
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
|
|
|
@ -4,17 +4,21 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
|||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/user.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
part 'fab.dart';
|
||||
part 'new_user.dart';
|
||||
|
@ -31,7 +35,6 @@ class UsersPage extends StatelessWidget {
|
|||
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
|
||||
final users = usersCubitState.users;
|
||||
final isEmpty = usersCubitState.isEmpty;
|
||||
|
||||
Widget child;
|
||||
|
||||
if (!isReady) {
|
||||
|
@ -46,14 +49,17 @@ class UsersPage extends StatelessWidget {
|
|||
)
|
||||
: ListView(
|
||||
children: [
|
||||
...users.map((user) => _User(user: user)),
|
||||
...users.map((user) => _User(user: user)).toList(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
child: BrandHeader(title: 'basis.users'.tr()),
|
||||
child: BrandHeader(
|
||||
title: 'basis.users'.tr(),
|
||||
hasFlashButton: true,
|
||||
),
|
||||
preferredSize: Size.fromHeight(52),
|
||||
),
|
||||
floatingActionButton: isReady ? _Fab() : null,
|
||||
|
|
|
@ -2,10 +2,8 @@ library elevation_extension;
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:selfprivacy/config/brand_shadow.dart';
|
||||
|
||||
extension ElevationExtension on BoxDecoration {
|
||||
BoxDecoration get ev8 => copyWith(boxShadow: [shadow8]);
|
||||
|
||||
BoxDecoration copyWith({
|
||||
Color? color,
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
extension TextExtension on Text {
|
||||
Text withColor(Color color) => Text(
|
||||
data!,
|
||||
key: this.key,
|
||||
strutStyle: this.strutStyle,
|
||||
textAlign: this.textAlign,
|
||||
textDirection: this.textDirection,
|
||||
locale: this.locale,
|
||||
softWrap: this.softWrap,
|
||||
overflow: this.overflow,
|
||||
textScaleFactor: this.textScaleFactor,
|
||||
maxLines: this.maxLines,
|
||||
semanticsLabel: this.semanticsLabel,
|
||||
textWidthBasis: textWidthBasis ?? this.textWidthBasis,
|
||||
style: this.style != null
|
||||
? this.style!.copyWith(color: color)
|
||||
: TextStyle(color: color),
|
||||
);
|
||||
|
||||
Text copyWith({
|
||||
Key? key,
|
||||
StrutStyle? strutStyle,
|
||||
TextAlign? textAlign,
|
||||
TextDirection? textDirection,
|
||||
Locale? locale,
|
||||
bool? softWrap,
|
||||
TextOverflow? overflow,
|
||||
double? textScaleFactor,
|
||||
int? maxLines,
|
||||
String? semanticsLabel,
|
||||
TextWidthBasis? textWidthBasis,
|
||||
TextStyle? style,
|
||||
}) {
|
||||
return Text(data!,
|
||||
key: key ?? this.key,
|
||||
strutStyle: strutStyle ?? this.strutStyle,
|
||||
textAlign: textAlign ?? this.textAlign,
|
||||
textDirection: textDirection ?? this.textDirection,
|
||||
locale: locale ?? this.locale,
|
||||
softWrap: softWrap ?? this.softWrap,
|
||||
overflow: overflow ?? this.overflow,
|
||||
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
|
||||
maxLines: maxLines ?? this.maxLines,
|
||||
semanticsLabel: semanticsLabel ?? this.semanticsLabel,
|
||||
textWidthBasis: textWidthBasis ?? this.textWidthBasis,
|
||||
style: style != null ? this.style?.merge(style) ?? style : this.style);
|
||||
}
|
||||
}
|
|
@ -1,138 +1,99 @@
|
|||
import 'dart:math';
|
||||
|
||||
var generator = PasswordGenerator();
|
||||
Random _rnd = Random();
|
||||
|
||||
String genPass() {
|
||||
generator.generate(8);
|
||||
return generator.getGeneratedValue();
|
||||
}
|
||||
|
||||
///Generates a password.
|
||||
///
|
||||
///The password [_generatedValue] is of a specified length, including letters [_letterGen] of mixed cases,
|
||||
///numbers [_numGen], and symbols[_symGen] depending on user choice.
|
||||
class PasswordGenerator {
|
||||
late bool _letterGen;
|
||||
late bool _numGen;
|
||||
late bool _symGen;
|
||||
late String _generatedValue;
|
||||
|
||||
///Constructor.
|
||||
///
|
||||
///[_letterGen] is true to make password generation possible from the opening of the application, and
|
||||
///[_generatedValue] is intialized to the value below so the text containing it can be first generated
|
||||
///upon users request
|
||||
PasswordGenerator() {
|
||||
_letterGen = true;
|
||||
_numGen = true;
|
||||
_symGen = false;
|
||||
_generatedValue = "Press Generate";
|
||||
}
|
||||
|
||||
///Call to generate a value, of [n] length
|
||||
void generate(int n) {
|
||||
//Discards the old value
|
||||
_generatedValue = "";
|
||||
|
||||
///Cannot generate a value without any character types selected
|
||||
if (!_letterGen && !_numGen && !_symGen) {
|
||||
_generatedValue = "No character type selected";
|
||||
return;
|
||||
}
|
||||
|
||||
///'Randomly' selectes caracter type to generate and append [toAppend] to [_generatedValue]
|
||||
// ignore: unnecessary_statements
|
||||
for (n; n > 0; n--) {
|
||||
String? toAppend;
|
||||
var random = new Random();
|
||||
|
||||
///loops until a valid character is generated, meaning the user has to check the character value
|
||||
///to be generated. 'Randomly' picks a character type.
|
||||
while (toAppend == null) {
|
||||
int selector = random.nextInt(3);
|
||||
|
||||
if (selector == 0) {
|
||||
toAppend = _generateLetter();
|
||||
} else if (selector == 1) {
|
||||
toAppend = _generateNumber();
|
||||
} else {
|
||||
toAppend = _generateSymbol();
|
||||
}
|
||||
}
|
||||
|
||||
_generatedValue += toAppend;
|
||||
toAppend = null;
|
||||
}
|
||||
}
|
||||
|
||||
///Generates a letter when called.
|
||||
String _generateLetter() {
|
||||
if (!_letterGen) return '';
|
||||
|
||||
///Finds the integer value for the range between a-z and A-Z, with [base] UTF-16 value for lowercase letters and
|
||||
///[baseUpper] UTF-16 value for uppercase letters
|
||||
int base = "a".codeUnitAt(0);
|
||||
int baseUpper = "A".codeUnitAt(0);
|
||||
int maxRand = ("z".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly selects between upper and lower case generation, randomly generates value from [maxRand], then adding base,
|
||||
///which creates a UTF-16 encoded character to be converted into a string of one character between a-z/A-Z.
|
||||
///This string is then returned.
|
||||
if (random.nextInt(2) == 0) {
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
} else {
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + baseUpper]);
|
||||
}
|
||||
}
|
||||
|
||||
///Generates a number when called
|
||||
String? _generateNumber() {
|
||||
if (!_numGen) return null;
|
||||
|
||||
///Finds the integer value for the range between 0-9
|
||||
int base = "0".codeUnitAt(0);
|
||||
int maxRand = ("9".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be converted into a
|
||||
///string of one character between 0-9. This string is then returned.
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
}
|
||||
|
||||
///Generates a symbol when called
|
||||
String? _generateSymbol() {
|
||||
if (!_symGen) return null;
|
||||
|
||||
///Finds the integer value for the range between symbols !-.
|
||||
|
||||
///(note) which includes symbols !"#$%&'()*+,=.
|
||||
int base = "!".codeUnitAt(0);
|
||||
int maxRand = (".".codeUnitAt(0) - base) + 1;
|
||||
Random random = new Random();
|
||||
|
||||
///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be
|
||||
///converted into a string of one character between !-. . This string is then returned.
|
||||
return String.fromCharCodes([random.nextInt(maxRand) + base]);
|
||||
}
|
||||
|
||||
///Toggles letter generation
|
||||
void checkLetterGen(bool value) {
|
||||
_letterGen = value;
|
||||
}
|
||||
|
||||
///Toggles number generation
|
||||
void checkNumGen(bool value) {
|
||||
_numGen = value;
|
||||
}
|
||||
|
||||
///Toggles symbol generation
|
||||
void checkSymGen(bool value) {
|
||||
_symGen = value;
|
||||
}
|
||||
|
||||
///Returns the generated value to be used by generator app
|
||||
String getGeneratedValue() {
|
||||
return _generatedValue;
|
||||
}
|
||||
typedef StringGeneratorFunction = String Function();
|
||||
|
||||
class StringGenerators {
|
||||
static const letters = 'abcdefghijklmnopqrstuvwxyz';
|
||||
static const numbers = '1234567890';
|
||||
static const symbols = '_';
|
||||
|
||||
static String getRandomString(
|
||||
int length, {
|
||||
hasLowercaseLetters = false,
|
||||
hasUppercaseLetters = false,
|
||||
hasNumbers = false,
|
||||
hasSymbols = false,
|
||||
isStrict = false,
|
||||
}) {
|
||||
var chars = '';
|
||||
if (hasLowercaseLetters) chars += letters;
|
||||
if (hasUppercaseLetters) chars += letters.toUpperCase();
|
||||
if (hasNumbers) chars += numbers;
|
||||
if (hasSymbols) chars += symbols;
|
||||
|
||||
assert(chars.isNotEmpty, 'chart empty');
|
||||
|
||||
if (!isStrict) {
|
||||
return genString(length, chars);
|
||||
}
|
||||
|
||||
var res = '';
|
||||
var loose = length;
|
||||
if (hasLowercaseLetters) {
|
||||
loose -= 1;
|
||||
res += genString(1, letters);
|
||||
}
|
||||
if (hasUppercaseLetters) {
|
||||
loose -= 1;
|
||||
res += genString(1, letters.toUpperCase());
|
||||
}
|
||||
if (hasNumbers) {
|
||||
loose -= 1;
|
||||
res += genString(1, numbers.toUpperCase());
|
||||
}
|
||||
if (hasSymbols) {
|
||||
loose -= 1;
|
||||
res += genString(1, symbols);
|
||||
}
|
||||
res += genString(loose, chars);
|
||||
|
||||
var shuffledlist = res.split('')..shuffle();
|
||||
return shuffledlist.join();
|
||||
}
|
||||
|
||||
static String genString(int length, String chars) {
|
||||
return String.fromCharCodes(
|
||||
Iterable.generate(
|
||||
length,
|
||||
(_) => chars.codeUnitAt(
|
||||
_rnd.nextInt(chars.length),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static StringGeneratorFunction userPassword = () => getRandomString(
|
||||
8,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
isStrict: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction passwordSalt = () => getRandomString(
|
||||
8,
|
||||
hasLowercaseLetters: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction simpleId = () => getRandomString(
|
||||
5,
|
||||
hasLowercaseLetters: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction dbPassword = () => getRandomString(
|
||||
40,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
hasSymbols: true,
|
||||
);
|
||||
|
||||
static StringGeneratorFunction dbStorageName = () => getRandomString(
|
||||
6,
|
||||
hasLowercaseLetters: true,
|
||||
hasUppercaseLetters: true,
|
||||
hasNumbers: true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import 'dart:math';
|
||||
|
||||
const _chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_';
|
||||
Random _rnd = Random();
|
||||
|
||||
String getRandomString(int length, [chars = _chars]) => String.fromCharCodes(
|
||||
Iterable.generate(
|
||||
length,
|
||||
(_) => chars.codeUnitAt(
|
||||
_rnd.nextInt(chars.length),
|
||||
),
|
||||
),
|
||||
);
|
319
pubspec.lock
|
@ -7,14 +7,14 @@ packages:
|
|||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "19.0.0"
|
||||
version: "22.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.7.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -28,28 +28,35 @@ packages:
|
|||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.2.0"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: asn1lib
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.8.1"
|
||||
basic_utils:
|
||||
dependency: "direct dev"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: basic_utils
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0-nullsafety.3"
|
||||
version: "3.5.0"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.1.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -63,56 +70,56 @@ packages:
|
|||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.1.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.7"
|
||||
version: "1.0.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.10"
|
||||
version: "3.0.0"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.12.2"
|
||||
version: "2.1.1"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.12"
|
||||
version: "7.1.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "5.1.0"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.0.4"
|
||||
version: "8.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -126,7 +133,7 @@ packages:
|
|||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -140,7 +147,7 @@ packages:
|
|||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.3.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -154,7 +161,7 @@ packages:
|
|||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
version: "4.1.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -168,14 +175,14 @@ packages:
|
|||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.0.3"
|
||||
crypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -184,33 +191,33 @@ packages:
|
|||
source: hosted
|
||||
version: "4.0.1"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
cubit_form:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cubit_form
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2-nullsafety.0"
|
||||
version: "1.0.18"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.0.3"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.3"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -252,7 +259,14 @@ packages:
|
|||
name: equatable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.3"
|
||||
extended_masked_text:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: extended_masked_text
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -266,14 +280,14 @@ packages:
|
|||
name: ffi
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
version: "6.1.2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -287,7 +301,7 @@ packages:
|
|||
name: fl_chart
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.35.0"
|
||||
version: "0.40.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -299,14 +313,14 @@ packages:
|
|||
name: flutter_bloc
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.2.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.0"
|
||||
version: "0.9.2"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -318,14 +332,21 @@ packages:
|
|||
name: flutter_markdown
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
version: "0.6.5"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_secure_storage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.2.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -336,13 +357,20 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: get_it
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
version: "7.2.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -356,42 +384,42 @@ packages:
|
|||
name: graphs
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "2.0.0"
|
||||
hive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.4"
|
||||
hive_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hive_flutter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.1.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.1"
|
||||
version: "0.13.3"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -419,7 +447,14 @@ packages:
|
|||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.3"
|
||||
ionicons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ionicons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -440,7 +475,14 @@ packages:
|
|||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.1.4"
|
||||
local_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: local_auth
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.7"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -455,13 +497,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
mask_text_input_formatter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mask_text_input_formatter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0-nullsafety.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -475,7 +510,7 @@ packages:
|
|||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.7.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -483,6 +518,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
modal_bottom_sheet:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: modal_bottom_sheet
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
nanoid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: nanoid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -496,7 +545,7 @@ packages:
|
|||
name: node_preamble
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.13"
|
||||
version: "2.0.1"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -510,7 +559,7 @@ packages:
|
|||
name: package_info
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -524,21 +573,21 @@ packages:
|
|||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.2"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
path_provider_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -552,42 +601,42 @@ packages:
|
|||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.3"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
version: "4.1.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pointycastle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.3.2"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -608,14 +657,14 @@ packages:
|
|||
name: process
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
version: "4.2.3"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "6.0.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -630,27 +679,83 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
rsa_encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rsa_encrypt
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
share_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
share_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
share_plus_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
share_plus_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.7"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -664,21 +769,21 @@ packages:
|
|||
name: shared_preferences_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.2.0"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -692,7 +797,7 @@ packages:
|
|||
name: shelf_static
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -711,7 +816,14 @@ packages:
|
|||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.3"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -732,7 +844,14 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
version: "1.8.1"
|
||||
ssh_key:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ssh_key
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -774,21 +893,21 @@ packages:
|
|||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.5"
|
||||
version: "1.17.10"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.19"
|
||||
version: "0.4.2"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.15"
|
||||
version: "0.4.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -796,6 +915,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tuple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -803,55 +929,48 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
unicons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: unicons
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.3"
|
||||
version: "6.0.9"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.1"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.4"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.4"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.0.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -865,42 +984,42 @@ packages:
|
|||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.0+1"
|
||||
version: "6.2.0"
|
||||
wakelock:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: wakelock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.0+2"
|
||||
version: "0.5.3+3"
|
||||
wakelock_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
version: "0.1.0+2"
|
||||
wakelock_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.2.1+2"
|
||||
wakelock_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.2.0+2"
|
||||
wakelock_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
version: "0.1.0+1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -914,7 +1033,7 @@ packages:
|
|||
name: web_socket_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "2.1.0"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -928,7 +1047,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.2.7"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -942,7 +1061,7 @@ packages:
|
|||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "5.1.2"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -951,5 +1070,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
dart: ">=2.13.4 <3.0.0"
|
||||
flutter: ">=2.2.3"
|
||||
|
|