add service page

fdroid
Kherel 2020-12-01 20:08:19 +01:00
parent cd02c75e2f
commit 90df52e895
17 changed files with 470 additions and 173 deletions

Binary file not shown.

View 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,
);
}
}

View File

@ -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),
];
} }

View File

@ -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);

View File

@ -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,
); );

View 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();

View 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();
}

View 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];
}

View File

@ -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,8 +12,10 @@ void main() {
runApp( runApp(
Localization( Localization(
child: BlocAndProviderConfig(
child: MyApp(), child: MyApp(),
), ),
),
); );
} }

View File

@ -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);
} }

View 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,
);
}
}

View File

@ -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'),
], ],

View File

@ -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,9 +15,8 @@ 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,
@ -28,7 +29,7 @@ class ServersPage extends StatelessWidget {
TextSpan( TextSpan(
text: text:
'Для устойчивости и приватности требует много учёток. Полная инструкция на ', 'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
style: body2TextStyle, style: body2Style,
), ),
BrandSpanButton.link( BrandSpanButton.link(
text: 'selfprivacy.org/start', text: 'selfprivacy.org/start',
@ -68,7 +69,7 @@ class ServersPage extends StatelessWidget {
children: [ children: [
TextSpan( TextSpan(
text: '1 Переходим по ссылке ', text: '1 Переходим по ссылке ',
style: bodyText1Style, style: body1Style,
), ),
BrandSpanButton.link( BrandSpanButton.link(
text: 'hetzner.com/sdfsdfsdfsdf', text: 'hetzner.com/sdfsdfsdfsdf',
@ -89,7 +90,7 @@ class ServersPage extends StatelessWidget {
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
''', ''',
style: bodyText1Style, style: body1Style,
), ),
], ],
), ),
@ -117,7 +118,7 @@ class ServersPage extends StatelessWidget {
children: [ children: [
TextSpan( TextSpan(
text: 'Зарегистрируйте домен в ', text: 'Зарегистрируйте домен в ',
style: body2TextStyle, style: body2Style,
), ),
BrandSpanButton.link( BrandSpanButton.link(
text: 'NameCheap', text: 'NameCheap',
@ -126,7 +127,7 @@ class ServersPage extends StatelessWidget {
TextSpan( TextSpan(
text: text:
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
style: body2TextStyle, style: body2Style,
), ),
], ],
), ),
@ -186,7 +187,6 @@ class ServersPage extends StatelessWidget {
) )
], ],
), ),
),
); );
} }
} }

View 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,
],
),
);
}
}

View File

@ -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);

View File

@ -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:

View File

@ -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: