mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-27 11:16:45 +00:00
add service page
This commit is contained in:
parent
cd02c75e2f
commit
90df52e895
Binary file not shown.
21
lib/config/bloc_config.dart
Normal file
21
lib/config/bloc_config.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
|
|
||||||
|
class BlocAndProviderConfig extends StatelessWidget {
|
||||||
|
const BlocAndProviderConfig({Key key, this.child}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider<ServicesCubit>(
|
||||||
|
create: (BuildContext context) => ServicesCubit(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,4 +28,17 @@ class BrandColors {
|
||||||
static const textColor2 = gray1;
|
static const textColor2 = gray1;
|
||||||
|
|
||||||
static get navBackground => white.withOpacity(0.8);
|
static get navBackground => white.withOpacity(0.8);
|
||||||
|
|
||||||
|
static const List<Color> uninitializedGradientColors = [
|
||||||
|
Color(0xFF555555),
|
||||||
|
Color(0xFFABABAB),
|
||||||
|
];
|
||||||
|
static const List<Color> stableGradientColors = [
|
||||||
|
Color(0xFF093CEF),
|
||||||
|
Color(0xFF14A1CB),
|
||||||
|
];
|
||||||
|
static const List<Color> warningGradientColors = [
|
||||||
|
Color(0xFFEF4E09),
|
||||||
|
Color(0xFFEFD135),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:selfprivacy/config/text_themes.dart';
|
||||||
|
|
||||||
import 'brand_colors.dart';
|
import 'brand_colors.dart';
|
||||||
|
|
||||||
var theme = ThemeData(
|
final theme = ThemeData(
|
||||||
primaryColor: BrandColors.primary,
|
primaryColor: BrandColors.primary,
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
|
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
|
||||||
|
@ -13,7 +13,9 @@ var theme = ThemeData(
|
||||||
headline1: headline1Style,
|
headline1: headline1Style,
|
||||||
headline2: headline2Style,
|
headline2: headline2Style,
|
||||||
caption: captionStyle,
|
caption: captionStyle,
|
||||||
bodyText1: bodyText1Style,
|
bodyText1: body1Style,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final brandPagePadding = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
|
||||||
|
|
|
@ -29,7 +29,7 @@ final captionStyle = GoogleFonts.inter(
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final bodyText1Style = defaultTextStyle;
|
final body1Style = defaultTextStyle;
|
||||||
final body2TextStyle = defaultTextStyle.copyWith(
|
final body2Style = defaultTextStyle.copyWith(
|
||||||
color: BrandColors.textColor2,
|
color: BrandColors.textColor2,
|
||||||
);
|
);
|
||||||
|
|
24
lib/logic/cubit/services/services_cubit.dart
Normal file
24
lib/logic/cubit/services/services_cubit.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
export 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
part 'services_state.dart';
|
||||||
|
|
||||||
|
class ServicesCubit extends Cubit<ServicesState> {
|
||||||
|
ServicesCubit() : super(ServicesState(all));
|
||||||
|
|
||||||
|
void connect(Service service) {
|
||||||
|
var newState = state.updateElement(service, ServiceStateType.stable);
|
||||||
|
emit(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final all = ServiceTypes.values
|
||||||
|
.map(
|
||||||
|
(type) => Service(
|
||||||
|
state: ServiceStateType.uninitialized,
|
||||||
|
type: type,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
23
lib/logic/cubit/services/services_state.dart
Normal file
23
lib/logic/cubit/services/services_state.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
part of 'services_cubit.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class ServicesState {
|
||||||
|
ServicesState(this.all);
|
||||||
|
|
||||||
|
final List<Service> all;
|
||||||
|
|
||||||
|
ServicesState updateElement(Service service, ServiceStateType newState) {
|
||||||
|
var newList = [...all];
|
||||||
|
var index = newList.indexOf(service);
|
||||||
|
newList[index] = service.updateState(newState);
|
||||||
|
return ServicesState(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Service> get connected => all
|
||||||
|
.where((service) => service.state != ServiceStateType.uninitialized)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<Service> get uninitialized => all
|
||||||
|
.where((service) => service.state == ServiceStateType.uninitialized)
|
||||||
|
.toList();
|
||||||
|
}
|
26
lib/logic/models/service.dart
Normal file
26
lib/logic/models/service.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
enum ServiceStateType { uninitialized, stable, warning }
|
||||||
|
enum ServiceTypes {
|
||||||
|
messanger,
|
||||||
|
mail,
|
||||||
|
passwordManager,
|
||||||
|
backup,
|
||||||
|
github,
|
||||||
|
cloud,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service extends Equatable {
|
||||||
|
const Service({this.state, this.type});
|
||||||
|
|
||||||
|
final ServiceStateType state;
|
||||||
|
final ServiceTypes type;
|
||||||
|
|
||||||
|
Service updateState(ServiceStateType newState) => Service(
|
||||||
|
state: newState,
|
||||||
|
type: type,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [state, type];
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
|
import 'config/bloc_config.dart';
|
||||||
import 'config/brand_theme.dart';
|
import 'config/brand_theme.dart';
|
||||||
import 'config/localization.dart';
|
import 'config/localization.dart';
|
||||||
|
|
||||||
|
@ -11,7 +12,9 @@ void main() {
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
Localization(
|
Localization(
|
||||||
child: MyApp(),
|
child: BlocAndProviderConfig(
|
||||||
|
child: MyApp(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,32 +23,26 @@ class BrandIcons {
|
||||||
|
|
||||||
static const IconData connection =
|
static const IconData connection =
|
||||||
IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData envelope =
|
||||||
|
IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData document =
|
static const IconData document =
|
||||||
IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData envelope =
|
static const IconData key =
|
||||||
IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData github =
|
static const IconData save =
|
||||||
IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData globe =
|
static const IconData globe =
|
||||||
IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData help =
|
static const IconData help =
|
||||||
IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData key =
|
|
||||||
IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
|
||||||
static const IconData messenger =
|
|
||||||
IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
|
||||||
static const IconData refresh =
|
static const IconData refresh =
|
||||||
IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData save =
|
|
||||||
IconData(0xe80b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
|
||||||
static const IconData settings =
|
static const IconData settings =
|
||||||
IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData share =
|
static const IconData share =
|
||||||
IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData triangle =
|
static const IconData triangle =
|
||||||
IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData upload =
|
|
||||||
IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
|
||||||
static const IconData server =
|
static const IconData server =
|
||||||
IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData box =
|
static const IconData box =
|
||||||
|
@ -57,4 +51,10 @@ class BrandIcons {
|
||||||
IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
static const IconData users =
|
static const IconData users =
|
||||||
IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData messanger =
|
||||||
|
IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData upload =
|
||||||
|
IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
|
static const IconData github =
|
||||||
|
IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||||
}
|
}
|
||||||
|
|
35
lib/ui/components/icon_status_mask/icon_status_mask.dart
Normal file
35
lib/ui/components/icon_status_mask/icon_status_mask.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
|
||||||
|
class IconStatusMaks extends StatelessWidget {
|
||||||
|
IconStatusMaks({this.child, this.status});
|
||||||
|
final Icon child;
|
||||||
|
|
||||||
|
final ServiceStateType status;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<Color> colors;
|
||||||
|
switch (status) {
|
||||||
|
case ServiceStateType.uninitialized:
|
||||||
|
colors = BrandColors.uninitializedGradientColors;
|
||||||
|
break;
|
||||||
|
case ServiceStateType.stable:
|
||||||
|
colors = BrandColors.stableGradientColors;
|
||||||
|
break;
|
||||||
|
case ServiceStateType.warning:
|
||||||
|
colors = BrandColors.warningGradientColors;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ShaderMask(
|
||||||
|
shaderCallback: (bounds) => LinearGradient(
|
||||||
|
begin: Alignment(-1, -0.8),
|
||||||
|
end: Alignment(0.9, 0.9),
|
||||||
|
colors: colors,
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
).createShader(bounds),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';
|
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';
|
||||||
import 'package:selfprivacy/ui/pages/servers/servers.dart';
|
import 'package:selfprivacy/ui/pages/servers/servers.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/services/services.dart';
|
||||||
|
|
||||||
class RootPage extends StatefulWidget {
|
class RootPage extends StatefulWidget {
|
||||||
const RootPage({Key key}) : super(key: key);
|
const RootPage({Key key}) : super(key: key);
|
||||||
|
@ -34,7 +35,7 @@ class _RootPageState extends State<RootPage>
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
children: [
|
children: [
|
||||||
ServersPage(),
|
ServersPage(),
|
||||||
Text('services'),
|
ServicesPage(),
|
||||||
Text('users'),
|
Text('users'),
|
||||||
Text('more'),
|
Text('more'),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
import 'package:selfprivacy/config/text_themes.dart';
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart';
|
import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/text_extension.dart';
|
import 'package:selfprivacy/utils/extensions/text_extension.dart';
|
||||||
|
export 'package:bloc/bloc.dart';
|
||||||
|
|
||||||
class ServersPage extends StatelessWidget {
|
class ServersPage extends StatelessWidget {
|
||||||
const ServersPage({Key key}) : super(key: key);
|
const ServersPage({Key key}) : super(key: key);
|
||||||
|
@ -13,71 +15,70 @@ class ServersPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: ListView(
|
||||||
child: ListView(
|
padding: brandPagePadding,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 30),
|
children: [
|
||||||
children: [
|
Text('Начало').caption,
|
||||||
Text('Начало').caption,
|
Text('SelfPrivacy').h1,
|
||||||
Text('SelfPrivacy').h1,
|
SizedBox(
|
||||||
SizedBox(
|
height: 10,
|
||||||
height: 10,
|
),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
|
||||||
|
style: body2Style,
|
||||||
|
),
|
||||||
|
BrandSpanButton.link(
|
||||||
|
text: 'selfprivacy.org/start',
|
||||||
|
urlString: 'https://selfprivacy.org/start',
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
RichText(
|
),
|
||||||
text: TextSpan(
|
SizedBox(height: 50),
|
||||||
children: [
|
BrandCard(
|
||||||
TextSpan(
|
child: Column(
|
||||||
text:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
|
children: [
|
||||||
style: body2TextStyle,
|
Image.asset('assets/images/logos/hetzner.png'),
|
||||||
),
|
SizedBox(height: 10),
|
||||||
BrandSpanButton.link(
|
Text('1. Подключите сервер Hetzner').h2,
|
||||||
text: 'selfprivacy.org/start',
|
SizedBox(height: 10),
|
||||||
urlString: 'https://selfprivacy.org/start',
|
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы')
|
||||||
),
|
.body2,
|
||||||
],
|
_MockForm(
|
||||||
),
|
hintText: 'Hetzner API Token',
|
||||||
),
|
),
|
||||||
SizedBox(height: 50),
|
SizedBox(height: 20),
|
||||||
BrandCard(
|
BrandButton.text(
|
||||||
child: Column(
|
onPressed: () => showModalBottomSheet<void>(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
context: context,
|
||||||
children: [
|
isScrollControlled: true,
|
||||||
Image.asset('assets/images/logos/hetzner.png'),
|
backgroundColor: Colors.transparent,
|
||||||
SizedBox(height: 10),
|
builder: (BuildContext context) {
|
||||||
Text('1. Подключите сервер Hetzner').h2,
|
return BrandModalSheet(
|
||||||
SizedBox(height: 10),
|
child: Column(
|
||||||
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы')
|
children: [
|
||||||
.body2,
|
Text('Как получить Hetzner API Token').h2,
|
||||||
_MockForm(
|
SizedBox(height: 20),
|
||||||
hintText: 'Hetzner API Token',
|
RichText(
|
||||||
),
|
text: TextSpan(
|
||||||
SizedBox(height: 20),
|
children: [
|
||||||
BrandButton.text(
|
TextSpan(
|
||||||
onPressed: () => showModalBottomSheet<void>(
|
text: '1 Переходим по ссылке ',
|
||||||
context: context,
|
style: body1Style,
|
||||||
isScrollControlled: true,
|
),
|
||||||
backgroundColor: Colors.transparent,
|
BrandSpanButton.link(
|
||||||
builder: (BuildContext context) {
|
text: 'hetzner.com/sdfsdfsdfsdf',
|
||||||
return BrandModalSheet(
|
urlString:
|
||||||
child: Column(
|
'https://hetzner.com/sdfsdfsdfsdf',
|
||||||
children: [
|
),
|
||||||
Text('Как получить Hetzner API Token').h2,
|
TextSpan(
|
||||||
SizedBox(height: 20),
|
text: '''
|
||||||
RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: '1 Переходим по ссылке ',
|
|
||||||
style: bodyText1Style,
|
|
||||||
),
|
|
||||||
BrandSpanButton.link(
|
|
||||||
text: 'hetzner.com/sdfsdfsdfsdf',
|
|
||||||
urlString:
|
|
||||||
'https://hetzner.com/sdfsdfsdfsdf',
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: '''
|
|
||||||
|
|
||||||
2 Заходим в созданный нами проект. Если такового - нет, значит создаём.
|
2 Заходим в созданный нами проект. Если такового - нет, значит создаём.
|
||||||
|
|
||||||
3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).
|
3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).
|
||||||
|
@ -88,104 +89,103 @@ class ServersPage extends StatelessWidget {
|
||||||
|
|
||||||
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
|
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
|
||||||
|
|
||||||
''',
|
''',
|
||||||
style: bodyText1Style,
|
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: body2TextStyle,
|
|
||||||
),
|
),
|
||||||
BrandSpanButton.link(
|
);
|
||||||
text: 'NameCheap',
|
},
|
||||||
urlString: 'https://www.namecheap.com',
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text:
|
|
||||||
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
|
|
||||||
style: body2TextStyle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_MockForm(
|
title: 'Как получить API Token',
|
||||||
hintText: 'Домен, например, selfprivacy.org',
|
),
|
||||||
submitButtonText: 'Проверить DNS',
|
],
|
||||||
),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
BrandButton.text(
|
|
||||||
onPressed: () {},
|
|
||||||
title: 'Как настроить DNS CloudFlare',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
BrandCard(
|
),
|
||||||
child: Column(
|
BrandCard(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Image.asset('assets/images/logos/cloudflare.png'),
|
children: [
|
||||||
SizedBox(height: 10),
|
Image.asset('assets/images/logos/namecheap.png'),
|
||||||
Text('3. Подключите CloudFlare DNS').h2,
|
SizedBox(height: 10),
|
||||||
SizedBox(height: 10),
|
Text('2. Настройте домен ').h2,
|
||||||
Text('Для управления DNS вашего домена').body2,
|
SizedBox(height: 10),
|
||||||
_MockForm(
|
RichText(
|
||||||
hintText: 'CloudFlare API Token',
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'Зарегистрируйте домен в ',
|
||||||
|
style: body2Style,
|
||||||
|
),
|
||||||
|
BrandSpanButton.link(
|
||||||
|
text: 'NameCheap',
|
||||||
|
urlString: 'https://www.namecheap.com',
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
|
||||||
|
style: body2Style,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 20),
|
),
|
||||||
BrandButton.text(
|
_MockForm(
|
||||||
onPressed: () {},
|
hintText: 'Домен, например, selfprivacy.org',
|
||||||
title: 'Как получить API Token',
|
submitButtonText: 'Проверить DNS',
|
||||||
),
|
),
|
||||||
],
|
SizedBox(height: 20),
|
||||||
),
|
BrandButton.text(
|
||||||
|
onPressed: () {},
|
||||||
|
title: 'Как настроить DNS CloudFlare',
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
BrandCard(
|
),
|
||||||
child: Column(
|
BrandCard(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Image.asset('assets/images/logos/aws.png'),
|
children: [
|
||||||
SizedBox(height: 10),
|
Image.asset('assets/images/logos/cloudflare.png'),
|
||||||
Text('4. Подключите Amazon AWS для бекапа').h2,
|
SizedBox(height: 10),
|
||||||
SizedBox(height: 10),
|
Text('3. Подключите CloudFlare DNS').h2,
|
||||||
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
|
SizedBox(height: 10),
|
||||||
.body2,
|
Text('Для управления DNS вашего домена').body2,
|
||||||
_MockForm(
|
_MockForm(
|
||||||
hintText: 'Amazon AWS Access Key',
|
hintText: 'CloudFlare API Token',
|
||||||
),
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
title: 'Как получить API Token',
|
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',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
110
lib/ui/pages/services/services.dart
Normal file
110
lib/ui/pages/services/services.dart
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
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_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 ServicesPage extends StatefulWidget {
|
||||||
|
ServicesPage({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ServicesPageState createState() => _ServicesPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ServicesPageState extends State<ServicesPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final serviceCubit = context.watch<ServicesCubit>();
|
||||||
|
final connected = serviceCubit.state.connected;
|
||||||
|
final uninitialized = serviceCubit.state.uninitialized;
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView(
|
||||||
|
padding: brandPagePadding,
|
||||||
|
children: [
|
||||||
|
Text('Сервисы').caption,
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,8 @@ extension TextExtension on Text {
|
||||||
Text get h2 => copyWith(style: headline2Style);
|
Text get h2 => copyWith(style: headline2Style);
|
||||||
Text get caption => copyWith(style: captionStyle);
|
Text get caption => copyWith(style: captionStyle);
|
||||||
|
|
||||||
Text get body2 => copyWith(style: body2TextStyle);
|
Text get body1 => copyWith(style: body1Style);
|
||||||
|
Text get body2 => copyWith(style: body2Style);
|
||||||
|
|
||||||
Text setKey(Key key) => copyWith(key: key);
|
Text setKey(Key key) => copyWith(key: key);
|
||||||
|
|
||||||
|
|
35
pubspec.lock
35
pubspec.lock
|
@ -22,6 +22,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0-nullsafety.1"
|
version: "2.5.0-nullsafety.1"
|
||||||
|
bloc:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bloc
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -85,6 +92,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.3"
|
version: "2.3.3"
|
||||||
|
equatable:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -111,6 +125,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_bloc:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_bloc
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.1"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -182,6 +203,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.3"
|
version: "1.3.0-nullsafety.3"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.4"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -259,6 +287,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.13"
|
version: "3.0.13"
|
||||||
|
provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: provider
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.3.2+2"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -11,7 +11,10 @@ dependencies:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.0
|
cupertino_icons: ^1.0.0
|
||||||
easy_localization: ^2.3.3
|
easy_localization: ^2.3.3
|
||||||
|
equatable: ^1.2.5
|
||||||
|
flutter_bloc: ^6.1.1
|
||||||
google_fonts: ^1.1.1
|
google_fonts: ^1.1.1
|
||||||
|
provider: ^4.3.2+2
|
||||||
url_launcher: ^5.7.10
|
url_launcher: ^5.7.10
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
Loading…
Reference in a new issue