update provider and bottom modal sheet

fdroid
Kherel 2020-12-10 21:33:19 +01:00
parent 600df97abf
commit 91aa2b860b
25 changed files with 881 additions and 191 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_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';
@ -14,19 +15,14 @@ class BlocAndProviderConfig extends StatelessWidget {
Widget build(BuildContext context) {
var platformBrightness =
SchedulerBinding.instance.window.platformBrightness;
var isDark = platformBrightness == Brightness.dark;
// var platformBrightness = Brightness.dark;
return MultiProvider(
providers: [
BlocProvider<AppSettingsCubit>(
create: (BuildContext context) => AppSettingsCubit(
isDarkModeOn: platformBrightness == Brightness.dark),
),
BlocProvider<ServicesCubit>(
create: (BuildContext context) => ServicesCubit(),
),
BlocProvider<UsersCubit>(
create: (BuildContext context) => UsersCubit(),
),
BlocProvider(create: (_) => AppSettingsCubit(isDarkModeOn: isDark)),
BlocProvider(create: (_) => ServicesCubit()),
BlocProvider(create: (_) => ProvidersCubit()),
BlocProvider(create: (_) => UsersCubit()),
],
child: child,
);

View File

@ -30,16 +30,6 @@ class BrandColors {
/// ![](https://www.colorhexa.com/0F8849.png)
static const Color green2 = Color(0xFF0F8849);
static const primary = blue;
static const headlineColor = black;
static const inactive = gray2;
static const scaffoldBackground = gray3;
static const inputInactive = gray4;
static const textColor1 = black;
static const textColor2 = gray1;
static const dividerColor = gray5;
static const warning = red;
static get navBackgroundLight => white.withOpacity(0.8);
static get navBackgroundDark => black.withOpacity(0.8);
@ -56,4 +46,18 @@ class BrandColors {
Color(0xFFEF4E09),
Color(0xFFEFD135),
];
static const primary = blue;
static const headlineColor = black;
static const inactive = gray2;
static const scaffoldBackground = gray3;
static const inputInactive = gray4;
static const textColor1 = black;
static const textColor2 = gray1;
static const dividerColor = gray5;
static const warning = red;
}

View File

@ -19,6 +19,26 @@ final ligtTheme = ThemeData(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(color: BrandColors.blue),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: BrandColors.red,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: BrandColors.red,
),
),
errorStyle: GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 12,
color: BrandColors.red,
),
),
),
textTheme: GoogleFonts.interTextTheme(
TextTheme(

View File

@ -0,0 +1,27 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
export 'package:selfprivacy/logic/models/state_types.dart';
export 'package:provider/provider.dart';
part 'providers_state.dart';
class ProvidersCubit extends Cubit<ProvidersState> {
ProvidersCubit() : super(ProvidersState(all));
void connect(ProviderModel provider) {
var newState = state.updateElement(provider, StateType.stable);
emit(newState);
}
}
final all = ProviderType.values
.map(
(type) => ProviderModel(
state: StateType.uninitialized,
type: type,
),
)
.toList();

View File

@ -0,0 +1,25 @@
part of 'providers_cubit.dart';
class ProvidersState extends Equatable {
const ProvidersState(this.all);
final List<ProviderModel> all;
ProvidersState updateElement(ProviderModel provider, StateType newState) {
var newList = [...all];
var index = newList.indexOf(provider);
newList[index] = provider.updateState(newState);
return ProvidersState(newList);
}
List<ProviderModel> get connected =>
all.where((service) => service.state != StateType.uninitialized).toList();
List<ProviderModel> get uninitialized =>
all.where((service) => service.state == StateType.uninitialized).toList();
bool get isFullyInitialized => uninitialized.isEmpty;
@override
List<Object> get props => all;
}

View File

@ -1,7 +1,11 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
export 'package:provider/provider.dart';
export 'package:selfprivacy/logic/models/state_types.dart';
part 'services_state.dart';
@ -9,7 +13,7 @@ class ServicesCubit extends Cubit<ServicesState> {
ServicesCubit() : super(ServicesState(all));
void connect(Service service) {
var newState = state.updateElement(service, ServiceStateType.stable);
var newState = state.updateElement(service, StateType.stable);
emit(newState);
}
}
@ -17,7 +21,7 @@ class ServicesCubit extends Cubit<ServicesState> {
final all = ServiceTypes.values
.map(
(type) => Service(
state: ServiceStateType.uninitialized,
state: StateType.uninitialized,
type: type,
),
)

View File

@ -1,12 +1,12 @@
part of 'services_cubit.dart';
@immutable
class ServicesState {
class ServicesState extends Equatable{
ServicesState(this.all);
final List<Service> all;
ServicesState updateElement(Service service, ServiceStateType newState) {
ServicesState updateElement(Service service, StateType newState) {
var newList = [...all];
var index = newList.indexOf(service);
newList[index] = service.updateState(newState);
@ -14,10 +14,13 @@ class ServicesState {
}
List<Service> get connected => all
.where((service) => service.state != ServiceStateType.uninitialized)
.where((service) => service.state != StateType.uninitialized)
.toList();
List<Service> get uninitialized => all
.where((service) => service.state == ServiceStateType.uninitialized)
.where((service) => service.state == StateType.uninitialized)
.toList();
@override
List<Object> get props => all;
}

View File

@ -1,9 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
enum ProviderTypes {
enum ProviderType {
server,
domain,
backup,
@ -12,10 +12,10 @@ enum ProviderTypes {
class ProviderModel extends Equatable {
const ProviderModel({this.state, this.type});
final ServiceStateType state;
final ProviderTypes type;
final StateType state;
final ProviderType type;
ProviderModel updateState(ServiceStateType newState) => ProviderModel(
ProviderModel updateState(StateType newState) => ProviderModel(
state: newState,
type: type,
);
@ -25,14 +25,14 @@ class ProviderModel extends Equatable {
IconData get icon {
switch (type) {
case ProviderTypes.server:
case ProviderType.server:
return BrandIcons.server;
case ProviderTypes.domain:
case ProviderType.domain:
return BrandIcons.globe;
break;
case ProviderTypes.backup:
case ProviderType.backup:
return BrandIcons.save;
}
return null;

View File

@ -1,6 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
enum ServiceStateType { uninitialized, stable, warning }
enum ServiceTypes {
messanger,
mail,
@ -12,10 +12,10 @@ enum ServiceTypes {
class Service extends Equatable {
const Service({this.state, this.type});
final ServiceStateType state;
final StateType state;
final ServiceTypes type;
Service updateState(ServiceStateType newState) => Service(
Service updateState(StateType newState) => Service(
state: newState,
type: type,
);

View File

@ -0,0 +1 @@
enum StateType { uninitialized, stable, warning }

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
var navigatorKey = GlobalKey<NavigatorState>();
class BrandModalSheet extends StatelessWidget {
const BrandModalSheet({
Key key,
@ -10,7 +12,7 @@ class BrandModalSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DraggableScrollableSheet(
minChildSize: 0.5,
minChildSize: 0.95,
initialChildSize: 1,
maxChildSize: 1,
builder: (context, scrollController) {
@ -20,20 +22,30 @@ class BrandModalSheet extends StatelessWidget {
child: Container(
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 32, bottom: 6),
GestureDetector(
onTap: () => Navigator.of(context).pop(),
behavior: HitTestBehavior.opaque,
child: Container(
height: 4,
width: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xFFE3E3E3).withOpacity(0.65),
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 - 32 - 4,
maxHeight: MediaQuery.of(context).size.height,
),
decoration: BoxDecoration(
borderRadius:
@ -41,7 +53,7 @@ class BrandModalSheet extends StatelessWidget {
color: Theme.of(context).scaffoldBackgroundColor,
),
width: double.infinity,
child: child,
child: child
),
],
),

View File

@ -17,16 +17,24 @@ class _BrandTabBarState extends State<BrandTabBar> {
@override
void initState() {
currentIndex = widget.controller.index;
widget.controller.addListener(() {
if (currentIndex != widget.controller.index) {
setState(() {
currentIndex = widget.controller.index;
});
}
});
widget.controller.addListener(_listener);
super.initState();
}
_listener() {
if (currentIndex != widget.controller.index) {
setState(() {
currentIndex = widget.controller.index;
});
}
}
@override
void dispose() {
widget.controller ?? widget.controller.removeListener(_listener);
super.dispose();
}
@override
Widget build(BuildContext context) {
final paddingBottom = MediaQuery.of(context).padding.bottom;

View File

@ -1,24 +1,24 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
class IconStatusMaks extends StatelessWidget {
IconStatusMaks({this.child, this.status});
final Icon child;
final ServiceStateType status;
final StateType status;
@override
Widget build(BuildContext context) {
List<Color> colors;
switch (status) {
case ServiceStateType.uninitialized:
case StateType.uninitialized:
colors = BrandColors.uninitializedGradientColors;
break;
case ServiceStateType.stable:
case StateType.stable:
colors = BrandColors.stableGradientColors;
break;
case ServiceStateType.warning:
case StateType.warning:
colors = BrandColors.warningGradientColors;
break;
}

View File

@ -0,0 +1,361 @@
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/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/models/provider.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/ui/components/brand_text/brand_text.dart';
class InitializingPage extends StatelessWidget {
const InitializingPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
var cubit = context.watch<ProvidersCubit>();
var connected = cubit.state.connected;
var uninitialized = cubit.state.uninitialized;
return Scaffold(
body: ListView(
padding: brandPagePadding1,
children: [
BrandText.h4('Начало'),
BrandText.h1('SelfPrivacy'),
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),
...connected.map((p) => getCard(context, p)).toList(),
...uninitialized.map((p) => getCard(context, p)).toList(),
],
),
);
}
void _showModal(BuildContext context, Widget widget) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return widget;
},
);
}
Widget getCard(BuildContext context, ProviderModel model) {
var cubit = context.watch<ProvidersCubit>();
if (model.state == StateType.stable) {
return _MockSuccess(type: model.type);
}
switch (model.type) {
case ProviderType.server:
return BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/hetzner.png'),
SizedBox(height: 10),
BrandText.h2('1. Подключите сервер Hetzner'),
SizedBox(height: 10),
BrandText.body2(
'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
_MockForm(
hintText: 'Hetzner API Token',
length: 2,
onPressed: () {
var provider = cubit.state.all
.firstWhere((p) => p.type == ProviderType.server);
cubit.connect(provider);
},
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
),
],
),
);
break;
case ProviderType.domain:
return BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/namecheap.png'),
SizedBox(height: 10),
BrandText.h2('2. Настройте домен'),
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',
length: 2,
onPressed: () {},
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как настроить DNS CloudFlare',
),
SizedBox(height: 10),
Image.asset('assets/images/logos/cloudflare.png'),
SizedBox(height: 10),
BrandText.h2('3. Подключите CloudFlare DNS'),
SizedBox(height: 10),
BrandText.body2('Для управления DNS вашего домена'),
_MockForm(
hintText: 'CloudFlare API Token',
length: 2,
onPressed: () {
var provider = cubit.state.all
.firstWhere((p) => p.type == ProviderType.domain);
cubit.connect(provider);
},
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
),
);
break;
case ProviderType.backup:
return BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/aws.png'),
SizedBox(height: 10),
BrandText.h2('4. Подключите Amazon AWS для бекапа'),
SizedBox(height: 10),
BrandText.body2(
'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'),
_MockForm(
hintText: 'Amazon AWS Access Key',
length: 2,
onPressed: () {
var provider = cubit.state.all
.firstWhere((p) => p.type == ProviderType.backup);
cubit.connect(provider);
},
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
),
);
}
return null;
}
}
class _HowHetzner extends StatelessWidget {
const _HowHetzner({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BrandModalSheet(
child: Padding(
padding: brandPagePadding2,
child: Column(
children: [
SizedBox(height: 40),
BrandText.h2('Как получить Hetzner API Token'),
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,
),
],
),
),
],
),
),
);
}
}
class _MockSuccess extends StatelessWidget {
const _MockSuccess({Key key, this.type}) : super(key: key);
final ProviderType type;
@override
Widget build(BuildContext context) {
String text;
switch (type) {
case ProviderType.server:
text = '1. Cервер подключен';
break;
case ProviderType.domain:
text = '2. Домен настроен';
break;
case ProviderType.backup:
text = '3. Резервное копирование настроенно';
break;
}
return BrandCard(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BrandText.h3(text),
Icon(
Icons.check,
color: BrandColors.green1,
),
],
),
);
}
}
class _MockForm extends StatefulWidget {
const _MockForm({
Key key,
@required this.hintText,
this.submitButtonText = 'Подключить',
@required this.onPressed,
@required this.length,
}) : super(key: key);
final String hintText;
final String submitButtonText;
final int length;
final VoidCallback onPressed;
@override
__MockFormState createState() => __MockFormState();
}
class __MockFormState extends State<_MockForm> {
String text = '';
bool _valid = true;
bool _touched = false;
onPressed() {
if (text.length == widget.length) {
setState(() {
_touched = true;
_valid = true;
widget.onPressed();
});
} else {
setState(() {
_touched = true;
_valid = false;
});
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(height: 20),
TextField(
onChanged: (value) {
if (_touched) {
if (value.length == widget.length) {
setState(() {
_valid = true;
text = value;
});
} else {
setState(() {
_valid = false;
text = value;
});
}
} else {
setState(() {
text = value;
});
}
},
decoration: InputDecoration(
hintText: widget.hintText,
errorText:
_valid ? null : 'Длинна должна быть ${widget.length} символа',
),
),
SizedBox(height: 20),
BrandButton.rised(
onPressed: _valid ? onPressed : null,
title: widget.submitButtonText,
),
],
);
}
}

View File

@ -10,14 +10,14 @@
// import 'package:selfprivacy/ui/pages/rootRoute.dart';
// import 'package:selfprivacy/utils/route_transitions/basic.dart';
// class OnboardingPage extends StatefulWidget {
// const OnboardingPage({Key key}) : super(key: key);
// class InitializingPage extends StatefulWidget {
// const InitializingPage({Key key}) : super(key: key);
// @override
// _OnboardingPageState createState() => _OnboardingPageState();
// _InitializingPageState createState() => _InitializingPageState();
// }
// class _OnboardingPageState extends State<OnboardingPage> {
// class _InitializingPageState extends State<InitializingPage> {
// PageController controller;
// var currentPage = 0;
@ -113,9 +113,10 @@
// children: [
// Image.asset('assets/images/logos/hetzner.png'),
// SizedBox(height: 10),
// Text('1. Подключите сервер Hetzner').h2,
// BrandText.h2('1. Подключите сервер Hetzner'),
// SizedBox(height: 10),
// Text('Здесь будут жить наши данные и SelfPrivacy-сервисы').body2,
// BrandText.body2(
// 'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
// _MockForm(
// onPressed: _nextPage,
// hintText: 'Hetzner API Token',
@ -137,7 +138,7 @@
// children: [
// Image.asset('assets/images/logos/namecheap.png'),
// SizedBox(height: 10),
// Text('2. Настройте домен ').h2,
// BrandText.h2('2. Настройте домен'),
// SizedBox(height: 10),
// RichText(
// text: TextSpan(
@ -179,9 +180,9 @@
// children: [
// Image.asset('assets/images/logos/cloudflare.png'),
// SizedBox(height: 10),
// Text('3. Подключите CloudFlare DNS').h2,
// BrandText.h2('3. Подключите CloudFlare DNS'),
// SizedBox(height: 10),
// Text('Для управления DNS вашего домена').body2,
// BrandText.body2('Для управления DNS вашего домена'),
// _MockForm(
// onPressed: _nextPage,
// hintText: 'CloudFlare API Token',
@ -202,10 +203,10 @@
// children: [
// Image.asset('assets/images/logos/aws.png'),
// SizedBox(height: 10),
// Text('4. Подключите Amazon AWS для бекапа').h2,
// BrandText.h2('4. Подключите Amazon AWS для бекапа'),
// SizedBox(height: 10),
// Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
// .body2,
// BrandText.body2(
// 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'),
// _MockForm(
// onPressed: () {
// Navigator.of(context)
@ -254,7 +255,7 @@
// child: Column(
// children: [
// SizedBox(height: 40),
// Text('Как получить Hetzner API Token').h2,
// BrandText.h2('Как получить Hetzner API Token'),
// SizedBox(height: 20),
// RichText(
// text: TextSpan(

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/logic/models/service.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_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
@ -20,10 +21,9 @@ class ProvidersPage extends StatefulWidget {
class _ProvidersPageState extends State<ProvidersPage> {
@override
Widget build(BuildContext context) {
final cards = ProviderTypes.values
.map((type) => _Card(
provider:
ProviderModel(state: ServiceStateType.stable, type: type)))
final cards = ProviderType.values
.map((type) =>
_Card(provider: ProviderModel(state: StateType.stable, type: type)))
.toList();
return Scaffold(
appBar: PreferredSize(
@ -49,16 +49,16 @@ class _Card extends StatelessWidget {
String stableText;
switch (provider.type) {
case ProviderTypes.server:
case ProviderType.server:
title = 'Сервер';
stableText = 'В норме';
break;
case ProviderTypes.domain:
case ProviderType.domain:
title = 'Домен';
message = 'example.com';
stableText = 'Домен настроен';
break;
case ProviderTypes.backup:
case ProviderType.backup:
message = '22 янв 2021 14:30';
title = 'Резервное копирование';
stableText = 'В норме';
@ -91,8 +91,7 @@ class _Card extends StatelessWidget {
BrandText.body2(message),
SizedBox(height: 10),
],
if (provider.state == ServiceStateType.stable)
BrandText.body2(stableText),
if (provider.state == StateType.stable) BrandText.body2(stableText),
],
),
),
@ -115,75 +114,84 @@ class _ProviderDetails extends StatelessWidget {
String title;
switch (provider.type) {
case ProviderTypes.server:
case ProviderType.server:
title = 'Сервер';
break;
case ProviderTypes.domain:
case ProviderType.domain:
title = 'Домен';
break;
case ProviderTypes.backup:
case ProviderType.backup:
title = 'Резервное копирование';
break;
}
return BrandModalSheet(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 4,
horizontal: 2,
),
child: PopupMenuButton<_PopupMenuItemType>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
onSelected: (_PopupMenuItemType result) {
switch (result) {
case _PopupMenuItemType.setting:
Navigator.of(context)
.pushReplacement(materialRoute(SettingsPage()));
break;
}
},
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [
PopupMenuItem<_PopupMenuItemType>(
value: _PopupMenuItemType.setting,
child: Container(
padding: EdgeInsets.only(left: 5),
child: Text('Настройки'),
),
),
],
),
),
),
Padding(
padding: brandPagePadding1,
child: Column(
child: Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (_) {
return materialRoute(
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 13),
IconStatusMaks(
status: provider.state,
child: Icon(provider.icon, size: 40, color: Colors.white),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 4,
horizontal: 2,
),
child: PopupMenuButton<_PopupMenuItemType>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
onSelected: (_PopupMenuItemType result) {
switch (result) {
case _PopupMenuItemType.setting:
navigatorKey.currentState
.push(materialRoute(SettingsPage()));
break;
}
},
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [
PopupMenuItem<_PopupMenuItemType>(
value: _PopupMenuItemType.setting,
child: Container(
padding: EdgeInsets.only(left: 5),
child: Text('Настройки'),
),
),
],
),
),
),
SizedBox(height: 10),
BrandText.h1(title),
SizedBox(height: 10),
BrandText.body1(statusText),
SizedBox(
height: 20,
),
Text('Статусы сервера и сервис провайдера и т.д.')
Padding(
padding: brandPagePadding1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 13),
IconStatusMaks(
status: provider.state,
child:
Icon(provider.icon, size: 40, color: Colors.white),
),
SizedBox(height: 10),
BrandText.h1(title),
SizedBox(height: 10),
BrandText.body1(statusText),
SizedBox(
height: 20,
),
Text('Статусы сервера и сервис провайдера и т.д.')
],
),
)
],
),
)
],
);
},
),
);
}

View File

@ -11,57 +11,51 @@ class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
child: BrandHeader(title: 'Настройки', hasBackButton: true),
preferredSize: Size.fromHeight(52),
return ListView(
padding: brandPagePadding2,
children: [
SizedBox(height: 10),
BrandHeader(title: 'Настройки', hasBackButton: true),
BrandDivider(),
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Allow Auto-upgrade',
value: 'Wether to allow automatic packages upgrades',
),
isActive: true,
),
body: ListView(
padding: brandPagePadding2,
children: [
BrandDivider(),
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Allow Auto-upgrade',
value: 'Wether to allow automatic packages upgrades',
),
isActive: true,
),
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Reboot after upgrade',
value: 'Reboot without prompt after applying updates',
),
isActive: false,
),
_Button(
onTap: () {},
child: _TextColumn(
title: 'Server Timezone',
value: 'Europe/Kyiv',
),
),
_Button(
onTap: () {},
child: _TextColumn(
title: 'Server Locale',
value: 'Default',
),
),
_Button(
onTap: () {},
child: _TextColumn(
hasWarning: true,
title: 'Factory Reset',
value: 'Restore default settings on your server',
),
)
],
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Reboot after upgrade',
value: 'Reboot without prompt after applying updates',
),
isActive: false,
),
),
_Button(
onTap: () {},
child: _TextColumn(
title: 'Server Timezone',
value: 'Europe/Kyiv',
),
),
_Button(
onTap: () {},
child: _TextColumn(
title: 'Server Locale',
value: 'Default',
),
),
_Button(
onTap: () {},
child: _TextColumn(
hasWarning: true,
title: 'Factory Reset',
value: 'Restore default settings on your server',
),
)
],
);
}
}

View File

@ -1,11 +1,16 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/pages/more/more.dart';
import 'package:selfprivacy/ui/pages/providers/providers.dart';
import 'package:selfprivacy/ui/pages/services/services.dart';
import 'package:selfprivacy/ui/pages/users/users.dart';
import 'initializing/initializing.dart';
class RootPage extends StatefulWidget {
const RootPage({Key key}) : super(key: key);
@ -25,20 +30,23 @@ class _RootPageState extends State<RootPage>
@override
void dispose() {
tabController.dispose();
super.dispose();
tabController.dispose();
}
@override
Widget build(BuildContext context) {
var isReady =
context.select((ProvidersCubit bloc) => bloc.state.isFullyInitialized);
return SafeArea(
child: Scaffold(
body: TabBarView(
controller: tabController,
children: [
ProvidersPage(),
ServicesPage(),
UsersPage(),
isReady ? ProvidersPage() : InitializingPage(),
isReady ? ServicesPage() : _NotReady(),
isReady ? UsersPage() : _NotReady(),
MorePage(),
],
),
@ -49,3 +57,20 @@ class _RootPageState extends State<RootPage>
);
}
}
class _NotReady extends StatelessWidget {
const _NotReady({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BrandText.h3('Not ready'),
BrandText.body2('Finish providers initialization first'),
],
),
);
}
}

View File

@ -93,7 +93,7 @@ class _Card extends StatelessWidget {
SizedBox(height: 10),
BrandText.h2(title),
SizedBox(height: 10),
if (service.state == ServiceStateType.uninitialized) ...[
if (service.state == StateType.uninitialized) ...[
BrandText.body1(description),
SizedBox(height: 10),
BrandButton.text(
@ -102,7 +102,7 @@ class _Card extends StatelessWidget {
context.read<ServicesCubit>().connect(service);
})
],
if (service.state == ServiceStateType.stable)
if (service.state == StateType.stable)
BrandText.body2('Подключен'),
],
),