forked from SelfPrivacy/selfprivacy.org.app
update provider and bottom modal sheet
parent
600df97abf
commit
91aa2b860b
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_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/services/services_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||||
|
|
||||||
|
@ -14,19 +15,14 @@ class BlocAndProviderConfig extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var platformBrightness =
|
var platformBrightness =
|
||||||
SchedulerBinding.instance.window.platformBrightness;
|
SchedulerBinding.instance.window.platformBrightness;
|
||||||
|
var isDark = platformBrightness == Brightness.dark;
|
||||||
// var platformBrightness = Brightness.dark;
|
// var platformBrightness = Brightness.dark;
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<AppSettingsCubit>(
|
BlocProvider(create: (_) => AppSettingsCubit(isDarkModeOn: isDark)),
|
||||||
create: (BuildContext context) => AppSettingsCubit(
|
BlocProvider(create: (_) => ServicesCubit()),
|
||||||
isDarkModeOn: platformBrightness == Brightness.dark),
|
BlocProvider(create: (_) => ProvidersCubit()),
|
||||||
),
|
BlocProvider(create: (_) => UsersCubit()),
|
||||||
BlocProvider<ServicesCubit>(
|
|
||||||
create: (BuildContext context) => ServicesCubit(),
|
|
||||||
),
|
|
||||||
BlocProvider<UsersCubit>(
|
|
||||||
create: (BuildContext context) => UsersCubit(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,16 +30,6 @@ class BrandColors {
|
||||||
/// ![](https://www.colorhexa.com/0F8849.png)
|
/// ![](https://www.colorhexa.com/0F8849.png)
|
||||||
static const Color green2 = Color(0xFF0F8849);
|
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 navBackgroundLight => white.withOpacity(0.8);
|
||||||
static get navBackgroundDark => black.withOpacity(0.8);
|
static get navBackgroundDark => black.withOpacity(0.8);
|
||||||
|
@ -56,4 +46,18 @@ class BrandColors {
|
||||||
Color(0xFFEF4E09),
|
Color(0xFFEF4E09),
|
||||||
Color(0xFFEFD135),
|
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;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,26 @@ final ligtTheme = ThemeData(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||||
borderSide: BorderSide(color: BrandColors.blue),
|
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: GoogleFonts.interTextTheme(
|
||||||
TextTheme(
|
TextTheme(
|
||||||
|
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:selfprivacy/logic/models/service.dart';
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
|
export 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
part 'services_state.dart';
|
part 'services_state.dart';
|
||||||
|
|
||||||
|
@ -9,7 +13,7 @@ class ServicesCubit extends Cubit<ServicesState> {
|
||||||
ServicesCubit() : super(ServicesState(all));
|
ServicesCubit() : super(ServicesState(all));
|
||||||
|
|
||||||
void connect(Service service) {
|
void connect(Service service) {
|
||||||
var newState = state.updateElement(service, ServiceStateType.stable);
|
var newState = state.updateElement(service, StateType.stable);
|
||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +21,7 @@ class ServicesCubit extends Cubit<ServicesState> {
|
||||||
final all = ServiceTypes.values
|
final all = ServiceTypes.values
|
||||||
.map(
|
.map(
|
||||||
(type) => Service(
|
(type) => Service(
|
||||||
state: ServiceStateType.uninitialized,
|
state: StateType.uninitialized,
|
||||||
type: type,
|
type: type,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
part of 'services_cubit.dart';
|
part of 'services_cubit.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class ServicesState {
|
class ServicesState extends Equatable{
|
||||||
ServicesState(this.all);
|
ServicesState(this.all);
|
||||||
|
|
||||||
final List<Service> all;
|
final List<Service> all;
|
||||||
|
|
||||||
ServicesState updateElement(Service service, ServiceStateType newState) {
|
ServicesState updateElement(Service service, StateType newState) {
|
||||||
var newList = [...all];
|
var newList = [...all];
|
||||||
var index = newList.indexOf(service);
|
var index = newList.indexOf(service);
|
||||||
newList[index] = service.updateState(newState);
|
newList[index] = service.updateState(newState);
|
||||||
|
@ -14,10 +14,13 @@ class ServicesState {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Service> get connected => all
|
List<Service> get connected => all
|
||||||
.where((service) => service.state != ServiceStateType.uninitialized)
|
.where((service) => service.state != StateType.uninitialized)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
List<Service> get uninitialized => all
|
List<Service> get uninitialized => all
|
||||||
.where((service) => service.state == ServiceStateType.uninitialized)
|
.where((service) => service.state == StateType.uninitialized)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => all;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/widgets.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';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
|
|
||||||
enum ProviderTypes {
|
enum ProviderType {
|
||||||
server,
|
server,
|
||||||
domain,
|
domain,
|
||||||
backup,
|
backup,
|
||||||
|
@ -12,10 +12,10 @@ enum ProviderTypes {
|
||||||
class ProviderModel extends Equatable {
|
class ProviderModel extends Equatable {
|
||||||
const ProviderModel({this.state, this.type});
|
const ProviderModel({this.state, this.type});
|
||||||
|
|
||||||
final ServiceStateType state;
|
final StateType state;
|
||||||
final ProviderTypes type;
|
final ProviderType type;
|
||||||
|
|
||||||
ProviderModel updateState(ServiceStateType newState) => ProviderModel(
|
ProviderModel updateState(StateType newState) => ProviderModel(
|
||||||
state: newState,
|
state: newState,
|
||||||
type: type,
|
type: type,
|
||||||
);
|
);
|
||||||
|
@ -25,14 +25,14 @@ class ProviderModel extends Equatable {
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ProviderTypes.server:
|
case ProviderType.server:
|
||||||
return BrandIcons.server;
|
return BrandIcons.server;
|
||||||
|
|
||||||
case ProviderTypes.domain:
|
case ProviderType.domain:
|
||||||
return BrandIcons.globe;
|
return BrandIcons.globe;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ProviderTypes.backup:
|
case ProviderType.backup:
|
||||||
return BrandIcons.save;
|
return BrandIcons.save;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
enum ServiceStateType { uninitialized, stable, warning }
|
|
||||||
enum ServiceTypes {
|
enum ServiceTypes {
|
||||||
messanger,
|
messanger,
|
||||||
mail,
|
mail,
|
||||||
|
@ -12,10 +12,10 @@ enum ServiceTypes {
|
||||||
class Service extends Equatable {
|
class Service extends Equatable {
|
||||||
const Service({this.state, this.type});
|
const Service({this.state, this.type});
|
||||||
|
|
||||||
final ServiceStateType state;
|
final StateType state;
|
||||||
final ServiceTypes type;
|
final ServiceTypes type;
|
||||||
|
|
||||||
Service updateState(ServiceStateType newState) => Service(
|
Service updateState(StateType newState) => Service(
|
||||||
state: newState,
|
state: newState,
|
||||||
type: type,
|
type: type,
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
enum StateType { uninitialized, stable, warning }
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
var navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
class BrandModalSheet extends StatelessWidget {
|
class BrandModalSheet extends StatelessWidget {
|
||||||
const BrandModalSheet({
|
const BrandModalSheet({
|
||||||
Key key,
|
Key key,
|
||||||
|
@ -10,7 +12,7 @@ class BrandModalSheet extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DraggableScrollableSheet(
|
return DraggableScrollableSheet(
|
||||||
minChildSize: 0.5,
|
minChildSize: 0.95,
|
||||||
initialChildSize: 1,
|
initialChildSize: 1,
|
||||||
maxChildSize: 1,
|
maxChildSize: 1,
|
||||||
builder: (context, scrollController) {
|
builder: (context, scrollController) {
|
||||||
|
@ -20,20 +22,30 @@ class BrandModalSheet extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
GestureDetector(
|
||||||
padding: EdgeInsets.only(top: 32, bottom: 6),
|
onTap: () => Navigator.of(context).pop(),
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 4,
|
width: double.infinity,
|
||||||
width: 30,
|
child: Center(
|
||||||
decoration: BoxDecoration(
|
child: Padding(
|
||||||
borderRadius: BorderRadius.circular(2),
|
padding: EdgeInsets.only(top: 132, bottom: 6),
|
||||||
color: Color(0xFFE3E3E3).withOpacity(0.65),
|
child: Container(
|
||||||
|
height: 4,
|
||||||
|
width: 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
color: Color(0xFFE3E3E3).withOpacity(0.65),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minHeight: MediaQuery.of(context).size.height - 32 - 4,
|
minHeight: MediaQuery.of(context).size.height - 32 - 4,
|
||||||
|
maxHeight: MediaQuery.of(context).size.height,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
|
@ -41,7 +53,7 @@ class BrandModalSheet extends StatelessWidget {
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: child,
|
child: child
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -17,16 +17,24 @@ class _BrandTabBarState extends State<BrandTabBar> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
currentIndex = widget.controller.index;
|
currentIndex = widget.controller.index;
|
||||||
widget.controller.addListener(() {
|
widget.controller.addListener(_listener);
|
||||||
if (currentIndex != widget.controller.index) {
|
|
||||||
setState(() {
|
|
||||||
currentIndex = widget.controller.index;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
super.initState();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final paddingBottom = MediaQuery.of(context).padding.bottom;
|
final paddingBottom = MediaQuery.of(context).padding.bottom;
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
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/logic/models/service.dart';
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
class IconStatusMaks extends StatelessWidget {
|
class IconStatusMaks extends StatelessWidget {
|
||||||
IconStatusMaks({this.child, this.status});
|
IconStatusMaks({this.child, this.status});
|
||||||
final Icon child;
|
final Icon child;
|
||||||
|
|
||||||
final ServiceStateType status;
|
final StateType status;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
List<Color> colors;
|
List<Color> colors;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ServiceStateType.uninitialized:
|
case StateType.uninitialized:
|
||||||
colors = BrandColors.uninitializedGradientColors;
|
colors = BrandColors.uninitializedGradientColors;
|
||||||
break;
|
break;
|
||||||
case ServiceStateType.stable:
|
case StateType.stable:
|
||||||
colors = BrandColors.stableGradientColors;
|
colors = BrandColors.stableGradientColors;
|
||||||
break;
|
break;
|
||||||
case ServiceStateType.warning:
|
case StateType.warning:
|
||||||
colors = BrandColors.warningGradientColors;
|
colors = BrandColors.warningGradientColors;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,14 +10,14 @@
|
||||||
// import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
// import 'package:selfprivacy/ui/pages/rootRoute.dart';
|
||||||
// import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
// import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
|
||||||
// class OnboardingPage extends StatefulWidget {
|
// class InitializingPage extends StatefulWidget {
|
||||||
// const OnboardingPage({Key key}) : super(key: key);
|
// const InitializingPage({Key key}) : super(key: key);
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
// _OnboardingPageState createState() => _OnboardingPageState();
|
// _InitializingPageState createState() => _InitializingPageState();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// class _OnboardingPageState extends State<OnboardingPage> {
|
// class _InitializingPageState extends State<InitializingPage> {
|
||||||
// PageController controller;
|
// PageController controller;
|
||||||
// var currentPage = 0;
|
// var currentPage = 0;
|
||||||
|
|
||||||
|
@ -113,9 +113,10 @@
|
||||||
// children: [
|
// children: [
|
||||||
// Image.asset('assets/images/logos/hetzner.png'),
|
// Image.asset('assets/images/logos/hetzner.png'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('1. Подключите сервер Hetzner').h2,
|
// BrandText.h2('1. Подключите сервер Hetzner'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('Здесь будут жить наши данные и SelfPrivacy-сервисы').body2,
|
// BrandText.body2(
|
||||||
|
// 'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
|
||||||
// _MockForm(
|
// _MockForm(
|
||||||
// onPressed: _nextPage,
|
// onPressed: _nextPage,
|
||||||
// hintText: 'Hetzner API Token',
|
// hintText: 'Hetzner API Token',
|
||||||
|
@ -137,7 +138,7 @@
|
||||||
// children: [
|
// children: [
|
||||||
// Image.asset('assets/images/logos/namecheap.png'),
|
// Image.asset('assets/images/logos/namecheap.png'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('2. Настройте домен ').h2,
|
// BrandText.h2('2. Настройте домен'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// RichText(
|
// RichText(
|
||||||
// text: TextSpan(
|
// text: TextSpan(
|
||||||
|
@ -179,9 +180,9 @@
|
||||||
// children: [
|
// children: [
|
||||||
// Image.asset('assets/images/logos/cloudflare.png'),
|
// Image.asset('assets/images/logos/cloudflare.png'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('3. Подключите CloudFlare DNS').h2,
|
// BrandText.h2('3. Подключите CloudFlare DNS'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('Для управления DNS вашего домена').body2,
|
// BrandText.body2('Для управления DNS вашего домена'),
|
||||||
// _MockForm(
|
// _MockForm(
|
||||||
// onPressed: _nextPage,
|
// onPressed: _nextPage,
|
||||||
// hintText: 'CloudFlare API Token',
|
// hintText: 'CloudFlare API Token',
|
||||||
|
@ -202,10 +203,10 @@
|
||||||
// children: [
|
// children: [
|
||||||
// Image.asset('assets/images/logos/aws.png'),
|
// Image.asset('assets/images/logos/aws.png'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('4. Подключите Amazon AWS для бекапа').h2,
|
// BrandText.h2('4. Подключите Amazon AWS для бекапа'),
|
||||||
// SizedBox(height: 10),
|
// SizedBox(height: 10),
|
||||||
// Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
|
// BrandText.body2(
|
||||||
// .body2,
|
// 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'),
|
||||||
// _MockForm(
|
// _MockForm(
|
||||||
// onPressed: () {
|
// onPressed: () {
|
||||||
// Navigator.of(context)
|
// Navigator.of(context)
|
||||||
|
@ -254,7 +255,7 @@
|
||||||
// child: Column(
|
// child: Column(
|
||||||
// children: [
|
// children: [
|
||||||
// SizedBox(height: 40),
|
// SizedBox(height: 40),
|
||||||
// Text('Как получить Hetzner API Token').h2,
|
// BrandText.h2('Как получить Hetzner API Token'),
|
||||||
// SizedBox(height: 20),
|
// SizedBox(height: 20),
|
||||||
// RichText(
|
// RichText(
|
||||||
// text: TextSpan(
|
// text: TextSpan(
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.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/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_card/brand_card.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
|
||||||
|
@ -20,10 +21,9 @@ class ProvidersPage extends StatefulWidget {
|
||||||
class _ProvidersPageState extends State<ProvidersPage> {
|
class _ProvidersPageState extends State<ProvidersPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cards = ProviderTypes.values
|
final cards = ProviderType.values
|
||||||
.map((type) => _Card(
|
.map((type) =>
|
||||||
provider:
|
_Card(provider: ProviderModel(state: StateType.stable, type: type)))
|
||||||
ProviderModel(state: ServiceStateType.stable, type: type)))
|
|
||||||
.toList();
|
.toList();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
|
@ -49,16 +49,16 @@ class _Card extends StatelessWidget {
|
||||||
String stableText;
|
String stableText;
|
||||||
|
|
||||||
switch (provider.type) {
|
switch (provider.type) {
|
||||||
case ProviderTypes.server:
|
case ProviderType.server:
|
||||||
title = 'Сервер';
|
title = 'Сервер';
|
||||||
stableText = 'В норме';
|
stableText = 'В норме';
|
||||||
break;
|
break;
|
||||||
case ProviderTypes.domain:
|
case ProviderType.domain:
|
||||||
title = 'Домен';
|
title = 'Домен';
|
||||||
message = 'example.com';
|
message = 'example.com';
|
||||||
stableText = 'Домен настроен';
|
stableText = 'Домен настроен';
|
||||||
break;
|
break;
|
||||||
case ProviderTypes.backup:
|
case ProviderType.backup:
|
||||||
message = '22 янв 2021 14:30';
|
message = '22 янв 2021 14:30';
|
||||||
title = 'Резервное копирование';
|
title = 'Резервное копирование';
|
||||||
stableText = 'В норме';
|
stableText = 'В норме';
|
||||||
|
@ -91,8 +91,7 @@ class _Card extends StatelessWidget {
|
||||||
BrandText.body2(message),
|
BrandText.body2(message),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
if (provider.state == ServiceStateType.stable)
|
if (provider.state == StateType.stable) BrandText.body2(stableText),
|
||||||
BrandText.body2(stableText),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -115,75 +114,84 @@ class _ProviderDetails extends StatelessWidget {
|
||||||
String title;
|
String title;
|
||||||
|
|
||||||
switch (provider.type) {
|
switch (provider.type) {
|
||||||
case ProviderTypes.server:
|
case ProviderType.server:
|
||||||
title = 'Сервер';
|
title = 'Сервер';
|
||||||
break;
|
break;
|
||||||
case ProviderTypes.domain:
|
case ProviderType.domain:
|
||||||
title = 'Домен';
|
title = 'Домен';
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ProviderTypes.backup:
|
case ProviderType.backup:
|
||||||
title = 'Резервное копирование';
|
title = 'Резервное копирование';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return BrandModalSheet(
|
return BrandModalSheet(
|
||||||
child: Column(
|
child: Navigator(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
key: navigatorKey,
|
||||||
children: [
|
initialRoute: '/',
|
||||||
Align(
|
onGenerateRoute: (_) {
|
||||||
alignment: Alignment.centerRight,
|
return materialRoute(
|
||||||
child: Padding(
|
Column(
|
||||||
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(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 13),
|
Align(
|
||||||
IconStatusMaks(
|
alignment: Alignment.centerRight,
|
||||||
status: provider.state,
|
child: Padding(
|
||||||
child: Icon(provider.icon, size: 40, color: Colors.white),
|
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),
|
Padding(
|
||||||
BrandText.h1(title),
|
padding: brandPagePadding1,
|
||||||
SizedBox(height: 10),
|
child: Column(
|
||||||
BrandText.body1(statusText),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
SizedBox(
|
children: [
|
||||||
height: 20,
|
SizedBox(height: 13),
|
||||||
),
|
IconStatusMaks(
|
||||||
Text('Статусы сервера и сервис провайдера и т.д.')
|
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('Статусы сервера и сервис провайдера и т.д.')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,57 +11,51 @@ class SettingsPage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return ListView(
|
||||||
child: Scaffold(
|
padding: brandPagePadding2,
|
||||||
appBar: PreferredSize(
|
children: [
|
||||||
child: BrandHeader(title: 'Настройки', hasBackButton: true),
|
SizedBox(height: 10),
|
||||||
preferredSize: Size.fromHeight(52),
|
BrandHeader(title: 'Настройки', hasBackButton: true),
|
||||||
|
BrandDivider(),
|
||||||
|
SwitcherBlock(
|
||||||
|
onChange: (_) {},
|
||||||
|
child: _TextColumn(
|
||||||
|
title: 'Allow Auto-upgrade',
|
||||||
|
value: 'Wether to allow automatic packages upgrades',
|
||||||
|
),
|
||||||
|
isActive: true,
|
||||||
),
|
),
|
||||||
body: ListView(
|
SwitcherBlock(
|
||||||
padding: brandPagePadding2,
|
onChange: (_) {},
|
||||||
children: [
|
child: _TextColumn(
|
||||||
BrandDivider(),
|
title: 'Reboot after upgrade',
|
||||||
SwitcherBlock(
|
value: 'Reboot without prompt after applying updates',
|
||||||
onChange: (_) {},
|
),
|
||||||
child: _TextColumn(
|
isActive: false,
|
||||||
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',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
_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',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.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_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/more/more.dart';
|
||||||
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
||||||
import 'package:selfprivacy/ui/pages/services/services.dart';
|
import 'package:selfprivacy/ui/pages/services/services.dart';
|
||||||
import 'package:selfprivacy/ui/pages/users/users.dart';
|
import 'package:selfprivacy/ui/pages/users/users.dart';
|
||||||
|
|
||||||
|
import 'initializing/initializing.dart';
|
||||||
|
|
||||||
|
|
||||||
class RootPage extends StatefulWidget {
|
class RootPage extends StatefulWidget {
|
||||||
const RootPage({Key key}) : super(key: key);
|
const RootPage({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@ -25,20 +30,23 @@ class _RootPageState extends State<RootPage>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
tabController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
tabController.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var isReady =
|
||||||
|
context.select((ProvidersCubit bloc) => bloc.state.isFullyInitialized);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
children: [
|
children: [
|
||||||
ProvidersPage(),
|
isReady ? ProvidersPage() : InitializingPage(),
|
||||||
ServicesPage(),
|
isReady ? ServicesPage() : _NotReady(),
|
||||||
UsersPage(),
|
isReady ? UsersPage() : _NotReady(),
|
||||||
MorePage(),
|
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'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ class _Card extends StatelessWidget {
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandText.h2(title),
|
BrandText.h2(title),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (service.state == ServiceStateType.uninitialized) ...[
|
if (service.state == StateType.uninitialized) ...[
|
||||||
BrandText.body1(description),
|
BrandText.body1(description),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
|
@ -102,7 +102,7 @@ class _Card extends StatelessWidget {
|
||||||
context.read<ServicesCubit>().connect(service);
|
context.read<ServicesCubit>().connect(service);
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
if (service.state == ServiceStateType.stable)
|
if (service.state == StateType.stable)
|
||||||
BrandText.body2('Подключен'),
|
BrandText.body2('Подключен'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue