forked from kherel/selfprivacy.org.app
parent
4875e3ee07
commit
80dee9dbab
Binary file not shown.
@ -0,0 +1,43 @@ |
||||
import 'package:bloc/bloc.dart'; |
||||
import 'package:equatable/equatable.dart'; |
||||
import 'package:selfprivacy/logic/models/user.dart'; |
||||
import 'package:selfprivacy/utils/password_generator.dart'; |
||||
export 'package:provider/provider.dart'; |
||||
|
||||
part 'users_state.dart'; |
||||
|
||||
class UsersCubit extends Cubit<UsersState> { |
||||
UsersCubit() : super(UsersState(initMockUsers)); |
||||
|
||||
void add(User user) { |
||||
var users = state.users; |
||||
users.add(user); |
||||
|
||||
emit(UsersState(users)); |
||||
} |
||||
|
||||
void remove(User user) { |
||||
var users = state.users; |
||||
users.remove(user); |
||||
|
||||
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()), |
||||
]; |
@ -0,0 +1,10 @@ |
||||
part of 'users_cubit.dart'; |
||||
|
||||
class UsersState extends Equatable { |
||||
const UsersState(this.users); |
||||
|
||||
final List<User> users; |
||||
|
||||
@override |
||||
List<Object> get props => users; |
||||
} |
@ -0,0 +1,20 @@ |
||||
import 'dart:ui'; |
||||
|
||||
import 'package:equatable/equatable.dart'; |
||||
import 'package:flutter/foundation.dart'; |
||||
import 'package:selfprivacy/utils/color_utils.dart'; |
||||
|
||||
class User extends Equatable { |
||||
User({ |
||||
@required this.login, |
||||
@required this.password, |
||||
}); |
||||
|
||||
final String login; |
||||
final String password; |
||||
|
||||
@override |
||||
List<Object> get props => [login, password]; |
||||
|
||||
Color get color => stringToColor(login); |
||||
} |
@ -0,0 +1,114 @@ |
||||
import 'package:flutter/material.dart'; |
||||
import 'package:selfprivacy/config/brand_theme.dart'; |
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; |
||||
import 'package:selfprivacy/logic/models/service.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_header/brand_header.dart'; |
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; |
||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; |
||||
import 'package:selfprivacy/utils/extensions/text_extension.dart'; |
||||
|
||||
class ProvidersPage extends StatefulWidget { |
||||
ProvidersPage({Key key}) : super(key: key); |
||||
|
||||
@override |
||||
_ProvidersPageState createState() => _ProvidersPageState(); |
||||
} |
||||
|
||||
class _ProvidersPageState extends State<ProvidersPage> { |
||||
@override |
||||
Widget build(BuildContext context) { |
||||
final serviceCubit = context.watch<ServicesCubit>(); |
||||
final connected = serviceCubit.state.connected; |
||||
final uninitialized = serviceCubit.state.uninitialized; |
||||
return Scaffold( |
||||
appBar: PreferredSize( |
||||
child: BrandHeader(title: 'Провайдеры'), |
||||
preferredSize: Size.fromHeight(52), |
||||
), |
||||
body: ListView( |
||||
padding: brandPagePadding2, |
||||
children: [ |
||||
SizedBox(height: 24), |
||||
...connected.map((service) => _Card(service: service)).toList(), |
||||
if (uninitialized.isNotEmpty) ...[ |
||||
Text('не подключены').body1, |
||||
SizedBox(height: 30), |
||||
], |
||||
...uninitialized.map((service) => _Card(service: service)).toList() |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
||||
|
||||
class _Card extends StatelessWidget { |
||||
const _Card({Key key, @required this.service}) : super(key: key); |
||||
|
||||
final Service service; |
||||
@override |
||||
Widget build(BuildContext context) { |
||||
String title; |
||||
IconData iconData; |
||||
String description; |
||||
|
||||
switch (service.type) { |
||||
case ServiceTypes.messanger: |
||||
iconData = BrandIcons.messanger; |
||||
title = 'Мессенджер'; |
||||
description = |
||||
'Delta Chat срфеТекст-текст описание. Если бы мне надо было обсудить что-то от чего зависит жизнь. Я бы выбрал Delta.Chat + свой почтовый сервер.'; |
||||
break; |
||||
case ServiceTypes.mail: |
||||
iconData = BrandIcons.envelope; |
||||
title = 'Почта'; |
||||
description = 'Электронная почта для семьи или компании '; |
||||
break; |
||||
case ServiceTypes.passwordManager: |
||||
iconData = BrandIcons.key; |
||||
title = 'Менеджер паролей'; |
||||
description = 'Надёжное хранилище для ваших паролей и ключей доступа'; |
||||
break; |
||||
case ServiceTypes.github: |
||||
iconData = BrandIcons.github; |
||||
title = 'Git сервер'; |
||||
description = 'Сервис для приватного хранения своих разработок'; |
||||
break; |
||||
case ServiceTypes.backup: |
||||
iconData = BrandIcons.save; |
||||
title = 'Резервное копирование'; |
||||
description = 'Обеспеченье целосности и сохранности ваших данных'; |
||||
break; |
||||
case ServiceTypes.cloud: |
||||
iconData = BrandIcons.upload; |
||||
title = 'Файловое Облако'; |
||||
description = 'Сервис для доступа к вашим файлам в любой точке мира'; |
||||
break; |
||||
} |
||||
return BrandCard( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
IconStatusMaks( |
||||
status: service.state, |
||||
child: Icon(iconData, size: 30, color: Colors.white), |
||||
), |
||||
SizedBox(height: 10), |
||||
Text(title).h2, |
||||
SizedBox(height: 10), |
||||
if (service.state == ServiceStateType.uninitialized) ...[ |
||||
Text(description).body1, |
||||
SizedBox(height: 10), |
||||
BrandButton.text( |
||||
title: 'Подключить', |
||||
onPressed: () { |
||||
context.read<ServicesCubit>().connect(service); |
||||
}) |
||||
], |
||||
if (service.state == ServiceStateType.stable) Text('Подключен').body1, |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
@ -1,230 +0,0 @@ |
||||
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/ui/components/brand_button/brand_button.dart'; |
||||
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; |
||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; |
||||
import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; |
||||
import 'package:selfprivacy/utils/extensions/text_extension.dart'; |
||||
export 'package:bloc/bloc.dart'; |
||||
|
||||
class ServersPage extends StatelessWidget { |
||||
const ServersPage({Key key}) : super(key: key); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Scaffold( |
||||
body: ListView( |
||||
padding: brandPagePadding1, |
||||
children: [ |
||||
Text('Начало').caption, |
||||
Text('SelfPrivacy').h1, |
||||
SizedBox( |
||||
height: 10, |
||||
), |
||||
RichText( |
||||
text: TextSpan( |
||||
children: [ |
||||
TextSpan( |
||||
text: |
||||
'Для устойчивости и приватности требует много учёток. Полная инструкция на ', |
||||
style: body2Style, |
||||
), |
||||
BrandSpanButton.link( |
||||
text: 'selfprivacy.org/start', |
||||
urlString: 'https://selfprivacy.org/start', |
||||
), |
||||
], |
||||
), |
||||
), |
||||
SizedBox(height: 50), |
||||
BrandCard( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Image.asset('assets/images/logos/hetzner.png'), |
||||
SizedBox(height: 10), |
||||
Text('1. Подключите сервер Hetzner').h2, |
||||
SizedBox(height: 10), |
||||
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы') |
||||
.body2, |
||||
_MockForm( |
||||
hintText: 'Hetzner API Token', |
||||
), |
||||
SizedBox(height: 20), |
||||
BrandButton.text( |
||||
onPressed: () => showModalBottomSheet<void>( |
||||
context: context, |
||||
isScrollControlled: true, |
||||
backgroundColor: Colors.transparent, |
||||
builder: (BuildContext context) { |
||||
return BrandModalSheet( |
||||
child: Column( |
||||
children: [ |
||||
Text('Как получить Hetzner API Token').h2, |
||||
SizedBox(height: 20), |
||||
RichText( |
||||
text: TextSpan( |
||||
children: [ |
||||
TextSpan( |
||||
text: '1 Переходим по ссылке ', |
||||
style: body1Style, |
||||
), |
||||
BrandSpanButton.link( |
||||
text: 'hetzner.com/sdfsdfsdfsdf', |
||||
urlString: |
||||
'https://hetzner.com/sdfsdfsdfsdf', |
||||
), |
||||
TextSpan( |
||||
text: ''' |
||||
|
||||
2 Заходим в созданный нами проект. Если такового - нет, значит создаём. |
||||
|
||||
3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). |
||||
|
||||
4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. |
||||
|
||||
5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. |
||||
|
||||
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. |
||||
|
||||
''', |
||||
style: body1Style, |
||||
), |
||||
], |
||||
), |
||||
), |
||||
], |
||||
), |
||||
); |
||||
}, |
||||
), |
||||
title: 'Как получить API Token', |
||||
), |
||||
], |
||||
), |
||||
), |
||||
BrandCard( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Image.asset('assets/images/logos/namecheap.png'), |
||||
SizedBox(height: 10), |
||||
Text('2. Настройте домен ').h2, |
||||
SizedBox(height: 10), |
||||
RichText( |
||||
text: TextSpan( |
||||
children: [ |
||||
TextSpan( |
||||
text: 'Зарегистрируйте домен в ', |
||||
style: body2Style, |
||||
), |
||||
BrandSpanButton.link( |
||||
text: 'NameCheap', |
||||
urlString: 'https://www.namecheap.com', |
||||
), |
||||
TextSpan( |
||||
text: |
||||
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', |
||||
style: body2Style, |
||||
), |
||||
], |
||||
), |
||||
), |
||||
_MockForm( |
||||
hintText: 'Домен, например, selfprivacy.org', |
||||
submitButtonText: 'Проверить DNS', |
||||
), |
||||
SizedBox(height: 20), |
||||
BrandButton.text( |
||||
onPressed: () {}, |
||||
title: 'Как настроить DNS CloudFlare', |
||||
), |
||||
], |
||||
), |
||||
), |
||||
BrandCard( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Image.asset('assets/images/logos/cloudflare.png'), |
||||
SizedBox(height: 10), |
||||
Text('3. Подключите CloudFlare DNS').h2, |
||||
SizedBox(height: 10), |
||||
Text('Для управления DNS вашего домена').body2, |
||||
_MockForm( |
||||
hintText: 'CloudFlare API Token', |
||||
), |
||||
SizedBox(height: 20), |
||||
BrandButton.text( |
||||
onPressed: () {}, |
||||
title: 'Как получить API Token', |
||||
), |
||||
], |
||||
), |
||||
), |
||||
BrandCard( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Image.asset('assets/images/logos/aws.png'), |
||||
SizedBox(height: 10), |
||||
Text('4. Подключите Amazon AWS для бекапа').h2, |
||||
SizedBox(height: 10), |
||||
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде') |
||||
.body2, |
||||
_MockForm( |
||||
hintText: 'Amazon AWS Access Key', |
||||
), |
||||
SizedBox(height: 20), |
||||
BrandButton.text( |
||||
onPressed: () {}, |
||||
title: 'Как получить API Token', |
||||
), |
||||
], |
||||
), |
||||
) |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
||||
|
||||
class _MockForm extends StatelessWidget { |
||||
const _MockForm({ |
||||
Key key, |
||||
@required this.hintText, |
||||
this.submitButtonText = 'Подключить', |
||||
}) : super(key: key); |
||||
|
||||
final String hintText; |
||||
final String submitButtonText; |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Column( |
||||
children: [ |
||||
SizedBox(height: 20), |
||||
TextField( |
||||
style: TextStyle(fontSize: 15, height: 1.6), |
||||
decoration: InputDecoration( |
||||
hintText: hintText, |
||||
contentPadding: EdgeInsets.all(16), |
||||
border: InputBorder.none, |
||||
enabledBorder: OutlineInputBorder( |
||||
borderRadius: BorderRadius.all(Radius.circular(4)), |
||||
borderSide: BorderSide(color: BrandColors.inputInactive), |
||||
), |
||||
focusedBorder: OutlineInputBorder( |
||||
borderRadius: BorderRadius.all(Radius.circular(4)), |
||||
borderSide: BorderSide(color: BrandColors.blue), |
||||
), |
||||
), |
||||
), |
||||
SizedBox(height: 20), |
||||
BrandButton.rised(onPressed: () {}, title: submitButtonText), |
||||
], |
||||
); |
||||
} |
||||
} |