master
Kherel Kechil 2021-07-29 07:24:42 +02:00
parent 21611e63c7
commit f53ad044c1
22 changed files with 338 additions and 133 deletions

View File

@ -19,12 +19,14 @@
"connect": "Connect", "connect": "Connect",
"domain": "Domain", "domain": "Domain",
"saving": "Saving..", "saving": "Saving..",
"nickname": "nickname", "nickname": "Nickname",
"loading": "Loading...", "loading": "Loading...",
"later": "I will setup it later", "later": "I will setup it later",
"reset": "Reset", "reset": "Reset",
"details": "Details", "details": "Details",
"no_data": "No data" "no_data": "No data",
"wait": "Wait",
"remove": "Remove"
}, },
"more": { "more": {
"_comment": "'More' tab", "_comment": "'More' tab",
@ -191,7 +193,6 @@
"23": "Enter a nickname and strong password", "23": "Enter a nickname and strong password",
"finish": "Everything is initialized", "finish": "Everything is initialized",
"checks": "Checks have been completed \n{} ouf of {}" "checks": "Checks have been completed \n{} ouf of {}"
}, },
"modals": { "modals": {
"_comment": "messages in modals", "_comment": "messages in modals",

View File

@ -24,7 +24,9 @@
"later": "Настрою потом", "later": "Настрою потом",
"reset": "Reset", "reset": "Reset",
"details": "Детальная информация", "details": "Детальная информация",
"no_data": "Нет данных" "no_data": "Нет данных",
"wait": "Ожидайте",
"remove": "Удалить"
}, },
"more": { "more": {
"_comment": "вкладка еще", "_comment": "вкладка еще",

View File

@ -6,6 +6,8 @@ PODS:
- Flutter - Flutter
- path_provider (0.0.1): - path_provider (0.0.1):
- Flutter - Flutter
- share_plus (0.0.1):
- Flutter
- shared_preferences (0.0.1): - shared_preferences (0.0.1):
- Flutter - Flutter
- url_launcher (0.0.1): - url_launcher (0.0.1):
@ -18,6 +20,7 @@ DEPENDENCIES:
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`) - package_info (from `.symlinks/plugins/package_info/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`)
@ -31,6 +34,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/package_info/ios" :path: ".symlinks/plugins/package_info/ios"
path_provider: path_provider:
:path: ".symlinks/plugins/path_provider/ios" :path: ".symlinks/plugins/path_provider/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences: shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios" :path: ".symlinks/plugins/shared_preferences/ios"
url_launcher: url_launcher:
@ -43,6 +48,7 @@ SPEC CHECKSUMS:
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f

View File

@ -17,7 +17,7 @@ class BlocAndProviderConfig extends StatelessWidget {
// SchedulerBinding.instance.window.platformBrightness; // SchedulerBinding.instance.window.platformBrightness;
// var isDark = platformBrightness == Brightness.dark; // var isDark = platformBrightness == Brightness.dark;
var isDark = false; var isDark = false;
var usersCubit = UsersCubit();
return MultiProvider( return MultiProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
@ -31,8 +31,8 @@ class BlocAndProviderConfig extends StatelessWidget {
create: (_) => AppConfigCubit()..load(), create: (_) => AppConfigCubit()..load(),
), ),
BlocProvider(create: (_) => ProvidersCubit()), BlocProvider(create: (_) => ProvidersCubit()),
BlocProvider(create: (_) => UsersCubit()), BlocProvider(create: (_) => usersCubit..load(), lazy: false),
BlocProvider(create: (_) => JobsCubit()), BlocProvider(create: (_) => JobsCubit(usersCubit)),
], ],
child: child, child: child,
); );

View File

@ -19,6 +19,8 @@ class HiveConfig {
Hive.registerAdapter(HetznerDataBaseAdapter()); Hive.registerAdapter(HetznerDataBaseAdapter());
await Hive.openBox(BNames.appSettings); await Hive.openBox(BNames.appSettings);
await Hive.openBox<User>(BNames.users);
var cipher = HiveAesCipher(await getEncriptedKey()); var cipher = HiveAesCipher(await getEncriptedKey());
await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); await Hive.openBox(BNames.appConfig, encryptionCipher: cipher);
@ -42,6 +44,7 @@ class BNames {
static String appConfig = 'appConfig'; static String appConfig = 'appConfig';
static String isDarkModeOn = 'isDarkModeOn'; static String isDarkModeOn = 'isDarkModeOn';
static String isOnbordingShowing = 'isOnbordingShowing'; static String isOnbordingShowing = 'isOnbordingShowing';
static String users = 'users';
static String appSettings = 'appSettings'; static String appSettings = 'appSettings';

View File

@ -95,7 +95,7 @@ class HetznerApi extends ApiMap {
var dbId = dbCreateResponse.data['volume']['id']; var dbId = dbCreateResponse.data['volume']['id'];
var data = jsonDecode( var data = jsonDecode(
'''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''
); );
Response serverCreateResponse = await client.post( Response serverCreateResponse = await client.post(

View File

@ -51,8 +51,9 @@ class ServerApi extends ApiMap {
'/createUser', '/createUser',
options: Options( options: Options(
headers: { headers: {
"X-Username": user.login, "X-User": user.login,
"X-Password": user.password, "X-Password":
'\$6\$${user.hashPassword.salt}\$${user.hashPassword.hash}',
}, },
), ),
); );
@ -68,4 +69,24 @@ class ServerApi extends ApiMap {
String get rootAddress => String get rootAddress =>
throw UnimplementedError('not used in with implementation'); throw UnimplementedError('not used in with implementation');
Future<bool> apply() async {
bool res;
Response response;
var client = await getClient();
try {
response = await client.get(
'/apply',
);
res = response.statusCode == HttpStatus.ok;
} catch (e) {
print(e);
res = false;
}
close(client);
return res;
}
} }

View File

@ -1,32 +1,53 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/server.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:selfprivacy/logic/models/jobs/job.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/user.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';
part 'jobs_state.dart'; part 'jobs_state.dart';
class JobsCubit extends Cubit<JobsState> { class JobsCubit extends Cubit<JobsState> {
JobsCubit() : super(JobsState.emtpy()); JobsCubit(this.usersCubit) : super(JobsStateEmpty());
final api = ServerApi(); final api = ServerApi();
final UsersCubit usersCubit;
void addJob(Job job) { void addJob(Job job) {
final newState = state.addJob(job); var newJobsList = <Job>[];
emit(newState); if (state is JobsStateWithJobs) {
newJobsList.addAll((state as JobsStateWithJobs).jobList);
}
newJobsList.add(job);
emit(JobsStateWithJobs(newJobsList));
} }
void removeJob(String id) { void removeJob(String id) {
final newState = state.removeById(id); final newState = (state as JobsStateWithJobs).removeById(id);
emit(newState); emit(newState);
} }
Future<void> applyAll() async { Future<void> applyAll() async {
for (var job in state.jobList) { if (state is JobsStateWithJobs) {
if (job is CreateUserJob) { var jobs = (state as JobsStateWithJobs).jobList;
// await api.createUser(job.user); emit(JobsStateLoading());
var newUsers = <User>[];
for (var job in jobs) {
if (job is CreateUserJob) {
newUsers.add(job.user);
await api.createUser(job.user);
}
} }
usersCubit.addUsers(newUsers);
await api.apply();
emit(JobsStateEmpty());
getIt<NavigationService>().navigator!.pop();
} }
emit(JobsState.emtpy());
} }
} }

View File

@ -1,25 +1,27 @@
part of 'jobs_cubit.dart'; part of 'jobs_cubit.dart';
class JobsState extends Equatable { abstract class JobsState extends Equatable {
const JobsState(this.jobList); @override
List<Object?> get props => [];
}
class JobsStateLoading extends JobsState {}
class JobsStateEmpty extends JobsState {}
class JobsStateWithJobs extends JobsState {
JobsStateWithJobs(this.jobList);
final List<Job> jobList; final List<Job> jobList;
static JobsState emtpy() => JobsState([]);
bool get isEmpty => jobList.isEmpty;
JobsState addJob(Job job) {
var newJobsList = [...jobList];
newJobsList.add(job);
return JobsState(newJobsList);
}
JobsState removeById(String id) { JobsState removeById(String id) {
var newJobsList = jobList.where((element) => element.id != id).toList(); var newJobsList = jobList.where((element) => element.id != id).toList();
return JobsState(newJobsList);
if (newJobsList.isEmpty) {
return JobsStateEmpty();
}
return JobsStateWithJobs(newJobsList);
} }
@override @override
List<Object> get props => jobList; List<Object?> get props => jobList;
} }

View File

@ -1,23 +1,35 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/logic/models/user.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';
part 'users_state.dart'; part 'users_state.dart';
class UsersCubit extends Cubit<UsersState> { class UsersCubit extends Cubit<UsersState> {
UsersCubit() : super(UsersState([])); UsersCubit() : super(UsersState(<User>[]));
Box<User> box = Hive.box<User>(BNames.users);
void addUser(User user) { void load() async {
var users = [...state.users]; var loadedUsers = box.values.toList();
users.add(user); if (loadedUsers.isNotEmpty) {
emit(UsersState(loadedUsers));
emit(UsersState(users)); }
} }
void remove(User? user) { void addUsers(List<User> users) async {
var newUserList = <User>[...state.users, ...users];
await box.addAll(users);
emit(UsersState(newUserList));
}
void remove(User user) async {
var users = [...state.users]; var users = [...state.users];
var index = users.indexOf(user);
users.remove(user); users.remove(user);
await box.deleteAt(index);
emit(UsersState(users)); emit(UsersState(users));
} }

View File

@ -23,40 +23,41 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
runApp( runApp(MyApp());
Localization(
child: BlocAndProviderConfig(
child: MyApp(),
),
),
);
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppSettingsState appSettings = context.watch<AppSettingsCubit>().state; return Localization(
child: BlocAndProviderConfig(
child: Builder(builder: (context) {
var appSettings = context.watch<AppSettingsCubit>().state;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changnig appbar color value: SystemUiOverlayStyle.light, // Manually changnig appbar color
child: MaterialApp( child: MaterialApp(
navigatorKey: getIt.get<NavigationService>().navigatorKey, navigatorKey: getIt.get<NavigationService>().navigatorKey,
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'SelfPrivacy', title: 'SelfPrivacy',
theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme, theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme,
home: appSettings.isOnbordingShowing home: appSettings.isOnbordingShowing
? OnboardingPage(nextPage: InitializingPage()) ? OnboardingPage(nextPage: InitializingPage())
: RootPage(), : RootPage(),
builder: (BuildContext context, Widget? widget) { builder: (BuildContext context, Widget? widget) {
Widget error = Text('...rendering error...'); Widget error = Text('...rendering error...');
if (widget is Scaffold || widget is Navigator) if (widget is Scaffold || widget is Navigator)
error = Scaffold(body: Center(child: error)); error = Scaffold(body: Center(child: error));
ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error; ErrorWidget.builder =
return widget!; (FlutterErrorDetails errorDetails) => error;
}, return widget!;
},
),
);
}),
), ),
); );
} }

View File

@ -0,0 +1,21 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
class BrandLoader {
static horizontal() => _HorizontalLoader();
}
class _HorizontalLoader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('basis.wait'.tr()),
SizedBox(height: 10),
LinearProgressIndicator(minHeight: 3),
],
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/config/text_themes.dart';
export 'package:selfprivacy/utils/extensions/text_extensions.dart';
enum TextType { enum TextType {
h1, // right now only at onboarding and opened providers h1, // right now only at onboarding and opened providers

View File

@ -1,10 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class JobsContent extends StatelessWidget { class JobsContent extends StatelessWidget {
@ -12,55 +15,71 @@ class JobsContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var jobs = context.watch<JobsCubit>().state; return BlocBuilder<JobsCubit, JobsState>(
return ListView( builder: (context, state) {
padding: paddingH15V0, late final List<Widget> widgets;
children: [ if (state is JobsStateEmpty) {
SizedBox(height: 15), widgets = [
Center( SizedBox(height: 80),
child: BrandText.h2( Center(child: BrandText.body1('jobs.empty'.tr())),
'jobs.title'.tr(), ];
), } else if (state is JobsStateLoading) {
), widgets = [
SizedBox(height: 20), SizedBox(height: 80),
if (jobs.isEmpty) BrandText.body1('jobs.empty'.tr()), BrandLoader.horizontal(),
if (!jobs.isEmpty) ...[ ];
...jobs.jobList } else if (state is JobsStateWithJobs) {
.map( widgets = [
(j) => Row( ...state.jobList
children: [ .map(
Expanded( (j) => Row(
child: BrandCards.small( children: [
child: Row( Expanded(
children: [ child: BrandCards.small(
BrandText.body1(j.title), child: Row(
], children: [
BrandText.body1(j.title),
],
),
), ),
), ),
), SizedBox(width: 10),
SizedBox(width: 10), ElevatedButton(
ElevatedButton( style: ElevatedButton.styleFrom(
style: ElevatedButton.styleFrom( primary: BrandColors.red1,
primary: BrandColors.red1, shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(10), ),
), ),
onPressed: () =>
context.read<JobsCubit>().removeJob(j.id),
child: Text('basis.remove'.tr()),
), ),
onPressed: () => ],
context.read<JobsCubit>().removeJob(j.id), ),
child: Text('Remove'), )
), .toList(),
], SizedBox(height: 20),
), BrandButton.rised(
) onPressed: () => context.read<JobsCubit>().applyAll(),
.toList(), text: 'jobs.start'.tr(),
SizedBox(height: 20), ),
BrandButton.rised( ];
onPressed: () => context.read<JobsCubit>().applyAll(), }
text: 'jobs.start'.tr(), return ListView(
), padding: paddingH15V0,
], children: [
], SizedBox(height: 15),
Center(
child: BrandText.h2(
'jobs.title'.tr(),
),
),
SizedBox(height: 20),
...widgets
],
);
},
); );
} }
} }

View File

@ -42,20 +42,18 @@ class _BrandFlashButtonState extends State<_BrandFlashButton>
super.dispose(); super.dispose();
} }
late bool wasPrevStateIsEmpty; bool wasPrevStateIsEmpty = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var hasNoJobs = context.watch<JobsCubit>().state.isEmpty;
wasPrevStateIsEmpty = hasNoJobs;
var icon = hasNoJobs ? Ionicons.flash_outline : Ionicons.flash;
return BlocListener<JobsCubit, JobsState>( return BlocListener<JobsCubit, JobsState>(
listener: (context, state) { listener: (context, state) {
if (wasPrevStateIsEmpty && state.jobList.isNotEmpty) { if (wasPrevStateIsEmpty && state is! JobsStateEmpty) {
wasPrevStateIsEmpty = false; wasPrevStateIsEmpty = false;
_animationController.forward(); _animationController.forward();
} else if (!wasPrevStateIsEmpty && state.jobList.isEmpty) { } else if (!wasPrevStateIsEmpty && state is JobsStateEmpty) {
wasPrevStateIsEmpty = true;
_animationController.reverse(); _animationController.reverse();
} }
}, },
@ -73,6 +71,7 @@ class _BrandFlashButtonState extends State<_BrandFlashButton>
animation: _colorTween, animation: _colorTween,
builder: (context, child) { builder: (context, child) {
var v = _animationController.value; var v = _animationController.value;
var icon = v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
return Transform.scale( return Transform.scale(
scale: 1 + (v < 0.5 ? v : 1 - v) * 2, scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
child: Icon( child: Icon(

View File

@ -82,7 +82,6 @@ class CpuChart extends StatelessWidget {
double appliedInterval, double appliedInterval,
double value, double value,
) { ) {
print(value);
if (value < 0) { if (value < 0) {
return false; return false;
} else if (value == 0) { } else if (value == 0) {

View File

@ -1,9 +1,9 @@
part of 'users.dart'; part of 'users.dart';
class _User extends StatelessWidget { class _User extends StatelessWidget {
const _User({Key? key, this.user}) : super(key: key); const _User({Key? key, required this.user}) : super(key: key);
final User? user; final User user;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return InkWell(
@ -24,12 +24,12 @@ class _User extends StatelessWidget {
width: 17, width: 17,
height: 17, height: 17,
decoration: BoxDecoration( decoration: BoxDecoration(
color: user!.color, color: user.color,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
), ),
SizedBox(width: 20), SizedBox(width: 20),
BrandText.h4(user!.login), BrandText.h4(user.login),
], ],
), ),
), ),

View File

@ -3,10 +3,10 @@ part of 'users.dart';
class _UserDetails extends StatelessWidget { class _UserDetails extends StatelessWidget {
const _UserDetails({ const _UserDetails({
Key? key, Key? key,
this.user, required this.user,
}) : super(key: key); }) : super(key: key);
final User? user; final User user;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -23,7 +23,7 @@ class _UserDetails extends StatelessWidget {
Container( Container(
height: 200, height: 200,
decoration: BoxDecoration( decoration: BoxDecoration(
color: user!.color, color: user.color,
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
top: Radius.circular(20), top: Radius.circular(20),
), ),
@ -116,7 +116,7 @@ class _UserDetails extends StatelessWidget {
horizontal: 15, horizontal: 15,
), ),
child: BrandText.h1( child: BrandText.h1(
user!.login, user.login,
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
)), )),
@ -133,14 +133,14 @@ class _UserDetails extends StatelessWidget {
Container( Container(
height: 40, height: 40,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: BrandText.h4('${user!.login}@$domainName'), child: BrandText.h4('${user.login}@$domainName'),
), ),
SizedBox(height: 14), SizedBox(height: 14),
BrandText.small('basis.password'.tr()), BrandText.small('basis.password'.tr()),
Container( Container(
height: 40, height: 40,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: BrandText.h4(user!.password), child: BrandText.h4(user.password),
), ),
SizedBox(height: 24), SizedBox(height: 24),
BrandDivider(), BrandDivider(),
@ -148,7 +148,10 @@ class _UserDetails extends StatelessWidget {
BrandButton.emptyWithIconText( BrandButton.emptyWithIconText(
title: 'users.send_regisration_data'.tr(), title: 'users.send_regisration_data'.tr(),
icon: Icon(BrandIcons.share), icon: Icon(BrandIcons.share),
onPressed: () {}, onPressed: () {
Share.share(
'login: ${user.login}, password: ${user.password}');
},
), ),
SizedBox(height: 20), SizedBox(height: 20),
], ],

View File

@ -17,6 +17,7 @@ import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/ui/helpers/modals.dart';
import 'package:selfprivacy/utils/ui_helpers.dart'; import 'package:selfprivacy/utils/ui_helpers.dart';
import 'package:share_plus/share_plus.dart';
part 'fab.dart'; part 'fab.dart';
part 'new_user.dart'; part 'new_user.dart';
@ -33,7 +34,6 @@ class UsersPage extends StatelessWidget {
var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized; var isReady = context.watch<AppConfigCubit>().state.isFullyInitilized;
final users = usersCubitState.users; final users = usersCubitState.users;
final isEmpty = usersCubitState.isEmpty; final isEmpty = usersCubitState.isEmpty;
Widget child; Widget child;
if (!isReady) { if (!isReady) {
@ -48,7 +48,7 @@ class UsersPage extends StatelessWidget {
) )
: ListView( : ListView(
children: [ children: [
...users.map((user) => _User(user: user)), ...users.map((user) => _User(user: user)).toList(),
], ],
); );
} }

View File

@ -0,0 +1,51 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
extension TextExtension on Text {
Text withColor(Color color) => Text(
data!,
key: this.key,
strutStyle: this.strutStyle,
textAlign: this.textAlign,
textDirection: this.textDirection,
locale: this.locale,
softWrap: this.softWrap,
overflow: this.overflow,
textScaleFactor: this.textScaleFactor,
maxLines: this.maxLines,
semanticsLabel: this.semanticsLabel,
textWidthBasis: textWidthBasis ?? this.textWidthBasis,
style: this.style != null
? this.style!.copyWith(color: color)
: TextStyle(color: color),
);
Text copyWith({
Key? key,
StrutStyle? strutStyle,
TextAlign? textAlign,
TextDirection? textDirection,
Locale? locale,
bool? softWrap,
TextOverflow? overflow,
double? textScaleFactor,
int? maxLines,
String? semanticsLabel,
TextWidthBasis? textWidthBasis,
TextStyle? style,
}) {
return Text(data!,
key: key ?? this.key,
strutStyle: strutStyle ?? this.strutStyle,
textAlign: textAlign ?? this.textAlign,
textDirection: textDirection ?? this.textDirection,
locale: locale ?? this.locale,
softWrap: softWrap ?? this.softWrap,
overflow: overflow ?? this.overflow,
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
maxLines: maxLines ?? this.maxLines,
semanticsLabel: semanticsLabel ?? this.semanticsLabel,
textWidthBasis: textWidthBasis ?? this.textWidthBasis,
style: style != null ? this.style?.merge(style) ?? style : this.style);
}
}

View File

@ -651,6 +651,48 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
share_plus:
dependency: "direct main"
description:
name: share_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
share_plus_linux:
dependency: transitive
description:
name: share_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
share_plus_macos:
dependency: transitive
description:
name: share_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
share_plus_web:
dependency: transitive
description:
name: share_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
share_plus_windows:
dependency: transitive
description:
name: share_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
shared_preferences: shared_preferences:
dependency: transitive dependency: transitive
description: description:

View File

@ -31,6 +31,7 @@ dependencies:
package_info: ^2.0.0 package_info: ^2.0.0
pretty_dio_logger: ^1.1.1 pretty_dio_logger: ^1.1.1
provider: ^5.0.0 provider: ^5.0.0
share_plus: ^2.1.4
unicons: ^1.0.2 unicons: ^1.0.2
url_launcher: ^6.0.2 url_launcher: ^6.0.2
wakelock: ^0.5.0+2 wakelock: ^0.5.0+2