From 90df52e89599f4bc9ec8df5a8891bd19c76fb32b Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 1 Dec 2020 20:08:19 +0100 Subject: [PATCH] add service page --- assets/fonts/BrandIcons.ttf | Bin 7072 -> 7040 bytes lib/config/bloc_config.dart | 21 ++ lib/config/brand_colors.dart | 13 + lib/config/brand_theme.dart | 6 +- lib/config/text_themes.dart | 4 +- lib/logic/cubit/services/services_cubit.dart | 24 ++ lib/logic/cubit/services/services_state.dart | 23 ++ lib/logic/models/service.dart | 26 ++ lib/main.dart | 5 +- .../components/brand_icons/brand_icons.dart | 20 +- .../icon_status_mask/icon_status_mask.dart | 35 ++ lib/ui/pages/rootRoute.dart | 3 +- lib/ui/pages/servers/servers.dart | 312 +++++++++--------- lib/ui/pages/services/services.dart | 110 ++++++ lib/utils/extensions/text_extension.dart | 3 +- pubspec.lock | 35 ++ pubspec.yaml | 3 + 17 files changed, 470 insertions(+), 173 deletions(-) create mode 100644 lib/config/bloc_config.dart create mode 100644 lib/logic/cubit/services/services_cubit.dart create mode 100644 lib/logic/cubit/services/services_state.dart create mode 100644 lib/logic/models/service.dart create mode 100644 lib/ui/components/icon_status_mask/icon_status_mask.dart create mode 100644 lib/ui/pages/services/services.dart diff --git a/assets/fonts/BrandIcons.ttf b/assets/fonts/BrandIcons.ttf index c4ce1ac8207a16e7a6ccfce0d8c37167427f15f9..d902699b569c8bce53addec5a75d7ee129f1a1e8 100644 GIT binary patch delta 2376 zcmY*aU2GHC6}~?+b7wsNV|&Jq6NBw|h6Hdnwqvt6*!%|A5TFDt>h=c=I0;KaAP&i{ zw3TVKE77V_cN=xtU9DQ$s#U9f>58fXL0|h&p^B;xeb{ZgqD6|TZPk~kQc&!k8)qLn zqtEx;IY0NF@0@#mw|uMocBcMggz$F}LN8yMt6x7H{5(L2yZ~65o_&4l=y!hdIYPn{ zgq+7Sjr!%ppC3L2+s^={8TcTN2~Q| z1Ro+qW7p@Ki^pDi(vMKjufgE=2*cMiomXLjr=91W{|e9E`9n{C2|xShd2@6PsdN>r z&?lddX1^H0sxCaEXI3=)Ekd8Kyp3z;z0DA>&@<>qU=c}FP z^gduy2D{(=zg}>@m~;P?9N8C0tD#kM)$4Q+T16o1t2URn9*jcBg}3M4_sKYJ!4_8e zHVBMcKosOHAOYkpAQ9v(APM9xAO>=%3nas+cYzcbjV@qOv}$&NR2c0pT1W!{a^OJR zG28{>j*%`9cZ_y{EEwG`5D(&Yfw(_^&Y&OS9()TwB@0xhQ^Ff!NGwTTm3}2XW@p%6 zzvArbDmui^CHtxrBb~cjE#mTXFE)&k$xxqEbv@L0kT!`5l_wCr4oy+I! zHS$r|4)W~_v7t;joXN%U-D1|W!;@x$ZhvaW@pAhwI78OK z&1si->3Au~(Si?^h&dY%9975tc06vktxt8;!4K|#Bc2B5^>uX5QBIMSefpz&9~*b+ zy|==9gch8M&TpZAI?%KUv=fb?uS05S14Cgv7ZMsN`URLK2^Ry986@l_NmoVF28l*o zZ}X$;xtlFl%n_Q})CW0Ig-RO~PpFmkO|50z(Ns+kL6rz;de3hr z-&?K?=g!JdP)Suaygd2-QmwZ1{$%@KfyDHIVXbff*XEnAzFfB5(J#;8+h$DFZAwHT zsyi_=+G~l5r&r5Sghv5p6Xa`aJZ1bBp*A}e#n$?SXSOOdUxpu zOk>b64=^WUD~wSp6a9sWLc|<8e!j^3@_~bu-LXW!sgL+hTJK4;!aGh>1H&4q`MODz zqxBQIkj|)zs0{2qnjNnt2Pfe;NtE3-vI)J?j%bFVMcNf6NsL*JBEsFs@88a6^fdV! z%A%hllr82k&l*w(1~S8N93&4o;cwXkA~*v+2JQl%=MSdLXM8j;leWQ4#d(4k8jZVv zA%7thk6ZTP+Ny#ug#EG$M3zgQ%MILa55l2j5grMemIrbB@Ya&0AzY;+P%S^eA4{4n zDVKuQE<#0HHN)5n8BtYF4vyhSLJMKsBv^K-L_>CB_nvF><=qLJX%w2+bqU+Q`xqhA z)UA+Csm`0#QC>MK5sWR1NdhIHm2y5a+10D?lg-H@76gKsoFZ5{`xV6zp;0J}wD?+* zuw$&yOI)`s>mdS1VJd~Gs)o(56_O)~{%BIr%m^Hhutk!RvTsBd1VIv&Oixcn5hbj# z7e}?;BmGhJYGu$8LPnfYChLwaY9ZTFPkqUjdxpa@3x@=mr97=qptP@#QlZxvO7t>= zX^O1u7;%)-r$UzPiiV?`w>B5N<$5Lys^dFcwf{&jl=|KyZ;mcEt}QiY=dU;9%k!6R z&NZ$r(yNWvg=T%JAx_WEUu+07joIsLp)s}4XwJyZ#^U0YYtv1!Ia6O~$cqbC>er@c w8&b2eu+&(f7w2CSAjVDcCVXfrbB$&b^o<4S=Jnb6`ekYQ%Hqt;i~Miye==;wYybcN delta 2388 zcmZ8ieQaA-6~7l9N$aGfwCNHxC21+C+qy~GG)a@R ztcc>O>(n78NSkdE(^giA0)I@9HUA)i7%&845)%?F{4t3k1X5r~bbla_n%JCYZxiA@ z|8&kh=XcM2_nvc)Z00%i(SH-D=ud#+G#G>j%&$TJF(5vDY5CdV#pXXD-d|vRcy|75b=$q%D|c?i zQb3^oh54oBpZxu!CkW|}fbcHDU>$8BC9L7cT#JGI6=qJw}1vAt;d?m{JFVBZ$WSk^xn3OZZw}- zz7~E6`p-YH1Lv2m583?GZ^dw}X34%3+Tb^A8(Oo8&<5Hd8{&rD(m#GMlHk>lfBi1` zEUptE0gxqW8_1GO8_1Gu8_1Ge8_1G;8_1Gk8%O{twSg>~tqoWbZP?pDmQ87+j$}|A zZ6J%_Yy(*YwGCwXU2P!C?`{KGemE}+2>h*Q6upN#Nt8UKpP_3^k$H)E!g1V7+)w!v z!VTe(@I<_dNOP4 z)yxteD>#}=iCE*gSR|TC7E~O@`CQ2F*~&98A!Qi2+L+=xwYB0dGKPtk4yg z=HGldptjKtPA+Oz6|33Az*cD->&lq3<)Z;b%kr3J|68Y{YC=y;)( zq~r(-CCz7P2~Q(2sfC3!BD%;7AK?Tm6B&F5{uC7-3|OT~RSiHoITDrvnu$0zx= zU-FD_%Cms&wf#k{ZE3yfmP%PX*TUl{WmuMilt zf&q*Fc-4YgDwsK3riEgmm`vf{F`O!4MYyYXlto5Yy&<3C39GK{<@4BKuXouUn1mQC zN2tiz1L^+J`}art(*a%$M&N9@NF;0Hc>?OtQHe`VWEJCU zN8n^^t_bgT`J7@f;3Q)A$%8&yq)(S@KCd$wQxhD<#O;R9=HqH2>Iei>Ca|qH;=ubbkri2T20YI1wpZ;4UobjdqWR1j)91-fB-if>M~QWBn_r^8r@g2f9e`?0m5OFE z%0uQ98fMBgnJQUjkS~>q3B~b0102m_u!7U0DTMy2Qc;Yw(qI$gmF zjL|bZKA!ln!m*Gs#o`lxkTHLtH#m8}a4*Of-BQZ2wOA!Rre zg<@0j%Zw~^1YE|ON`U1!Ei5Ze<4wDt=Qv3U1)avD$%nUxdJ3mSUiLZhTc_7<9~ydL z?R4XvE`d@dw@Zl}x_IOH&wu%BS<@@i*YVdKMi7cl(iP2cDsoukI5q&f!yy~8#7Taq zX5hCD-CjF=?DgKN(4jkqZa;i`?S-MudtDMIikgzU_WaGcqbbguxN_^crLiuw`bX*Q zRztc@AHaFU(Ox(Wu?^`qoIocjN!mBYzc07uks zYoU;~!_e@WX6$r@A2)u1J2k`58Zh1wDrCIgOd&)r8=xs4DO#gvjeHRFY)yI$JksL* z!0_PB$9If0O#0brZ@LiLnhX`wUMPSNacVGW^B#^gK3IRvcAwTsbF;Z<{rRCmbP@8H zr3&StGAVqsnT$oDahZ@CYX>IehFG=ggA`evBn;)rWC`2qQVz^%-h3~HodX%NskX|x zjG^A4pE1>rnXkV3+KZP4Y&%DH`{mrBFLVbpsd{Kv+U@bUGd;n~w&9s)zc_uw&D))7 zbZ=Itp3JU@5pv{0#Z#38jqX3$7_>{eE=#}BG9}M=CTQ11ZCnxUdcea#!;r|4{tJV; zr?+}Xnl`kH?#h_ZOBQ?*5Yv37p3wlvKwRj<~B prP}iH%-qxxyEI*0tO?7DGu64N*&26cVRpWHZgn{5g}-%#{tGMS)!qOA diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart new file mode 100644 index 0000000000..88bc3903c9 --- /dev/null +++ b/lib/config/bloc_config.dart @@ -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( + create: (BuildContext context) => ServicesCubit(), + ), + ], + child: child, + ); + } +} diff --git a/lib/config/brand_colors.dart b/lib/config/brand_colors.dart index 028d5c2c3a..3b7ca03c1f 100644 --- a/lib/config/brand_colors.dart +++ b/lib/config/brand_colors.dart @@ -28,4 +28,17 @@ class BrandColors { static const textColor2 = gray1; static get navBackground => white.withOpacity(0.8); + + static const List uninitializedGradientColors = [ + Color(0xFF555555), + Color(0xFFABABAB), + ]; + static const List stableGradientColors = [ + Color(0xFF093CEF), + Color(0xFF14A1CB), + ]; + static const List warningGradientColors = [ + Color(0xFFEF4E09), + Color(0xFFEFD135), + ]; } diff --git a/lib/config/brand_theme.dart b/lib/config/brand_theme.dart index e08c09d1a0..7c8458a3e1 100644 --- a/lib/config/brand_theme.dart +++ b/lib/config/brand_theme.dart @@ -4,7 +4,7 @@ import 'package:selfprivacy/config/text_themes.dart'; import 'brand_colors.dart'; -var theme = ThemeData( +final theme = ThemeData( primaryColor: BrandColors.primary, brightness: Brightness.light, scaffoldBackgroundColor: BrandColors.scaffoldBackground, @@ -13,7 +13,9 @@ var theme = ThemeData( headline1: headline1Style, headline2: headline2Style, caption: captionStyle, - bodyText1: bodyText1Style, + bodyText1: body1Style, ), ), ); + +final brandPagePadding = EdgeInsets.symmetric(horizontal: 15, vertical: 30); diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart index a871d0e8ae..4a3c0c84f2 100644 --- a/lib/config/text_themes.dart +++ b/lib/config/text_themes.dart @@ -29,7 +29,7 @@ final captionStyle = GoogleFonts.inter( color: BrandColors.headlineColor, ); -final bodyText1Style = defaultTextStyle; -final body2TextStyle = defaultTextStyle.copyWith( +final body1Style = defaultTextStyle; +final body2Style = defaultTextStyle.copyWith( color: BrandColors.textColor2, ); diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart new file mode 100644 index 0000000000..95c1169ae0 --- /dev/null +++ b/lib/logic/cubit/services/services_cubit.dart @@ -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 { + 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(); diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart new file mode 100644 index 0000000000..4057b3e339 --- /dev/null +++ b/lib/logic/cubit/services/services_state.dart @@ -0,0 +1,23 @@ +part of 'services_cubit.dart'; + +@immutable +class ServicesState { + ServicesState(this.all); + + final List all; + + ServicesState updateElement(Service service, ServiceStateType newState) { + var newList = [...all]; + var index = newList.indexOf(service); + newList[index] = service.updateState(newState); + return ServicesState(newList); + } + + List get connected => all + .where((service) => service.state != ServiceStateType.uninitialized) + .toList(); + + List get uninitialized => all + .where((service) => service.state == ServiceStateType.uninitialized) + .toList(); +} diff --git a/lib/logic/models/service.dart b/lib/logic/models/service.dart new file mode 100644 index 0000000000..5167eeaa4b --- /dev/null +++ b/lib/logic/models/service.dart @@ -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 get props => [state, type]; +} diff --git a/lib/main.dart b/lib/main.dart index 3b3a6ab857..e2c11ca208 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'config/bloc_config.dart'; import 'config/brand_theme.dart'; import 'config/localization.dart'; @@ -11,7 +12,9 @@ void main() { runApp( Localization( - child: MyApp(), + child: BlocAndProviderConfig( + child: MyApp(), + ), ), ); } diff --git a/lib/ui/components/brand_icons/brand_icons.dart b/lib/ui/components/brand_icons/brand_icons.dart index dc4bcf5810..72be2f904b 100644 --- a/lib/ui/components/brand_icons/brand_icons.dart +++ b/lib/ui/components/brand_icons/brand_icons.dart @@ -23,32 +23,26 @@ class BrandIcons { static const IconData connection = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData envelope = + IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData envelope = + static const IconData key = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData github = + static const IconData save = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData globe = IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData help = 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 = IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData save = - IconData(0xe80b, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData settings = IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData share = IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData triangle = IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData upload = - IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData server = IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData box = @@ -57,4 +51,10 @@ class BrandIcons { IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData users = 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); } diff --git a/lib/ui/components/icon_status_mask/icon_status_mask.dart b/lib/ui/components/icon_status_mask/icon_status_mask.dart new file mode 100644 index 0000000000..91ce636984 --- /dev/null +++ b/lib/ui/components/icon_status_mask/icon_status_mask.dart @@ -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 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, + ); + } +} diff --git a/lib/ui/pages/rootRoute.dart b/lib/ui/pages/rootRoute.dart index a7cfaa8210..0851c0d286 100644 --- a/lib/ui/pages/rootRoute.dart +++ b/lib/ui/pages/rootRoute.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.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/services/services.dart'; class RootPage extends StatefulWidget { const RootPage({Key key}) : super(key: key); @@ -34,7 +35,7 @@ class _RootPageState extends State controller: tabController, children: [ ServersPage(), - Text('services'), + ServicesPage(), Text('users'), Text('more'), ], diff --git a/lib/ui/pages/servers/servers.dart b/lib/ui/pages/servers/servers.dart index 924ba6b385..486106b744 100644 --- a/lib/ui/pages/servers/servers.dart +++ b/lib/ui/pages/servers/servers.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; import 'package:selfprivacy/utils/extensions/text_extension.dart'; +export 'package:bloc/bloc.dart'; class ServersPage extends StatelessWidget { const ServersPage({Key key}) : super(key: key); @@ -13,71 +15,70 @@ class ServersPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - body: Container( - child: ListView( - padding: EdgeInsets.symmetric(horizontal: 15, vertical: 30), - children: [ - Text('Начало').caption, - Text('SelfPrivacy').h1, - SizedBox( - height: 10, + body: ListView( + padding: brandPagePadding, + children: [ + Text('Начало').caption, + Text('SelfPrivacy').h1, + SizedBox( + height: 10, + ), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: + 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', + style: body2Style, + ), + BrandSpanButton.link( + text: 'selfprivacy.org/start', + urlString: 'https://selfprivacy.org/start', + ), + ], ), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', - style: body2TextStyle, - ), - BrandSpanButton.link( - text: 'selfprivacy.org/start', - urlString: 'https://selfprivacy.org/start', - ), - ], - ), - ), - SizedBox(height: 50), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/hetzner.png'), - SizedBox(height: 10), - Text('1. Подключите сервер Hetzner').h2, - SizedBox(height: 10), - Text('Здесь будут жить наши данные и SelfPrivacy-сервисы') - .body2, - _MockForm( - hintText: 'Hetzner API Token', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return BrandModalSheet( - child: Column( - children: [ - Text('Как получить Hetzner API Token').h2, - SizedBox(height: 20), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: '1 Переходим по ссылке ', - style: bodyText1Style, - ), - BrandSpanButton.link( - text: 'hetzner.com/sdfsdfsdfsdf', - urlString: - 'https://hetzner.com/sdfsdfsdfsdf', - ), - TextSpan( - text: ''' - + ), + SizedBox(height: 50), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/hetzner.png'), + SizedBox(height: 10), + Text('1. Подключите сервер Hetzner').h2, + SizedBox(height: 10), + Text('Здесь будут жить наши данные и SelfPrivacy-сервисы') + .body2, + _MockForm( + hintText: 'Hetzner API Token', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return BrandModalSheet( + child: Column( + children: [ + Text('Как получить Hetzner API Token').h2, + SizedBox(height: 20), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '1 Переходим по ссылке ', + style: body1Style, + ), + BrandSpanButton.link( + text: 'hetzner.com/sdfsdfsdfsdf', + urlString: + 'https://hetzner.com/sdfsdfsdfsdf', + ), + TextSpan( + text: ''' + 2 Заходим в созданный нами проект. Если такового - нет, значит создаём. 3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). @@ -88,104 +89,103 @@ class ServersPage extends StatelessWidget { 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. - ''', - style: bodyText1Style, - ), - ], - ), + ''', + style: body1Style, + ), + ], ), - ], - ), - ); - }, - ), - title: 'Как получить API Token', - ), - ], - ), - ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/namecheap.png'), - SizedBox(height: 10), - Text('2. Настройте домен ').h2, - SizedBox(height: 10), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'Зарегистрируйте домен в ', - style: body2TextStyle, + ), + ], ), - BrandSpanButton.link( - text: 'NameCheap', - urlString: 'https://www.namecheap.com', - ), - TextSpan( - text: - ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', - style: body2TextStyle, - ), - ], - ), + ); + }, ), - _MockForm( - hintText: 'Домен, например, selfprivacy.org', - submitButtonText: 'Проверить DNS', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как настроить DNS CloudFlare', - ), - ], - ), + title: 'Как получить API Token', + ), + ], ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/cloudflare.png'), - SizedBox(height: 10), - Text('3. Подключите CloudFlare DNS').h2, - SizedBox(height: 10), - Text('Для управления DNS вашего домена').body2, - _MockForm( - hintText: 'CloudFlare API Token', + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/namecheap.png'), + SizedBox(height: 10), + Text('2. Настройте домен ').h2, + SizedBox(height: 10), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'Зарегистрируйте домен в ', + style: body2Style, + ), + BrandSpanButton.link( + text: 'NameCheap', + urlString: 'https://www.namecheap.com', + ), + TextSpan( + text: + ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', + style: body2Style, + ), + ], ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', - ), - ], - ), + ), + _MockForm( + hintText: 'Домен, например, selfprivacy.org', + submitButtonText: 'Проверить DNS', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как настроить DNS CloudFlare', + ), + ], ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/aws.png'), - SizedBox(height: 10), - Text('4. Подключите Amazon AWS для бекапа').h2, - SizedBox(height: 10), - Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде') - .body2, - _MockForm( - hintText: 'Amazon AWS Access Key', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', - ), - ], - ), - ) - ], - ), + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/cloudflare.png'), + SizedBox(height: 10), + Text('3. Подключите CloudFlare DNS').h2, + SizedBox(height: 10), + Text('Для управления DNS вашего домена').body2, + _MockForm( + hintText: 'CloudFlare API Token', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как получить API Token', + ), + ], + ), + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/aws.png'), + SizedBox(height: 10), + Text('4. Подключите Amazon AWS для бекапа').h2, + SizedBox(height: 10), + Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде') + .body2, + _MockForm( + hintText: 'Amazon AWS Access Key', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как получить API Token', + ), + ], + ), + ) + ], ), ); } diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart new file mode 100644 index 0000000000..a60f5a9388 --- /dev/null +++ b/lib/ui/pages/services/services.dart @@ -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 { + @override + Widget build(BuildContext context) { + final serviceCubit = context.watch(); + 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().connect(service); + }) + ], + if (service.state == ServiceStateType.stable) Text('Подключен').body1, + ], + ), + ); + } +} diff --git a/lib/utils/extensions/text_extension.dart b/lib/utils/extensions/text_extension.dart index 6094bcf4c1..98b02bda9e 100644 --- a/lib/utils/extensions/text_extension.dart +++ b/lib/utils/extensions/text_extension.dart @@ -9,7 +9,8 @@ extension TextExtension on Text { Text get h2 => copyWith(style: headline2Style); 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); diff --git a/pubspec.lock b/pubspec.lock index f3b1f207ba..998c80bf27 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: @@ -85,6 +92,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.3" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.5" fake_async: dependency: transitive description: @@ -111,6 +125,13 @@ packages: description: flutter source: sdk 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: dependency: "direct dev" description: @@ -182,6 +203,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" path: dependency: transitive description: @@ -259,6 +287,13 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ad190df3e1..58875a1517 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,10 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.0 easy_localization: ^2.3.3 + equatable: ^1.2.5 + flutter_bloc: ^6.1.1 google_fonts: ^1.1.1 + provider: ^4.3.2+2 url_launcher: ^5.7.10 dev_dependencies: