- Refactor Hive boxes

- Delete SSH generation leftovers
- Migrate users box to an encrypted box
pull/90/head
Inex Code 2022-05-17 23:08:28 +03:00
parent 19bc780db1
commit bf79fb1adf
22 changed files with 134 additions and 156 deletions

View File

@ -27,7 +27,7 @@ class BlocAndProviderConfig extends StatelessWidget {
BlocProvider(
create: (_) => AppSettingsCubit(
isDarkModeOn: isDark,
isOnbordingShowing: true,
isOnboardingShowing: true,
)..load(),
),
BlocProvider(create: (_) => serverInstallationCubit, lazy: false),

View File

@ -2,7 +2,6 @@ import 'package:get_it/get_it.dart';
import 'package:selfprivacy/logic/get_it/api_config.dart';
import 'package:selfprivacy/logic/get_it/console.dart';
import 'package:selfprivacy/logic/get_it/navigation.dart';
import 'package:selfprivacy/logic/get_it/ssh.dart';
import 'package:selfprivacy/logic/get_it/timer.dart';
export 'package:selfprivacy/logic/get_it/api_config.dart';
@ -17,7 +16,6 @@ Future<void> getItSetup() async {
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
getIt.registerSingleton<TimerModel>(TimerModel());
getIt.registerSingleton<SSHModel>(SSHModel()..init());
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
await getIt.allReady();

View File

@ -19,15 +19,22 @@ class HiveConfig {
Hive.registerAdapter(BackblazeBucketAdapter());
Hive.registerAdapter(ServerVolumeAdapter());
await Hive.openBox(BNames.appSettings);
await Hive.openBox<User>(BNames.users);
await Hive.openBox(BNames.servicesState);
await Hive.openBox(BNames.appSettingsBox);
var cipher = HiveAesCipher(await getEncryptedKey(BNames.key));
await Hive.openBox(BNames.appConfig, encryptionCipher: cipher);
var cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey));
var sshCipher = HiveAesCipher(await getEncryptedKey(BNames.sshEnckey));
await Hive.openBox(BNames.sshConfig, encryptionCipher: sshCipher);
await Hive.openBox<User>(BNames.usersDeprecated);
await Hive.openBox<User>(BNames.users, encryptionCipher: cipher);
Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
if (deprecatedUsers.isNotEmpty) {
Box<User> users = Hive.box<User>(BNames.users);
users.addAll(deprecatedUsers.values.toList());
deprecatedUsers.clear();
}
await Hive.openBox(BNames.serverInstallation, encryptionCipher: cipher);
}
static Future<Uint8List> getEncryptedKey(String encKey) async {
@ -43,33 +50,65 @@ class HiveConfig {
}
}
/// Mappings for the different boxes and their keys
class BNames {
static String appConfig = 'appConfig';
/// App settings box. Contains app settings like [isDarkModeOn], [isOnboardingShowing]
static String appSettingsBox = 'appSettings';
/// A boolean field of [appSettingsBox] box.
static String isDarkModeOn = 'isDarkModeOn';
static String isOnbordingShowing = 'isOnbordingShowing';
static String users = 'users';
/// A boolean field of [appSettingsBox] box.
static String isOnboardingShowing = 'isOnboardingShowing';
/// Encryption key to decrypt [serverInstallation] and [users] box.
static String serverInstallationEncryptionKey = 'key';
/// Server installation box. Contains server details and provider tokens.
static String serverInstallation = 'appConfig';
/// A List<String> field of [serverInstallation] box.
static String rootKeys = 'rootKeys';
static String appSettings = 'appSettings';
static String servicesState = 'servicesState';
static String key = 'key';
static String sshEnckey = 'sshEngkey';
/// A boolean field of [serverInstallation] box.
static String hasFinalChecked = 'hasFinalChecked';
/// A boolean field of [serverInstallation] box.
static String isServerStarted = 'isServerStarted';
/// A [ServerDomain] field of [serverInstallation] box.
static String serverDomain = 'cloudFlareDomain';
/// A String field of [serverInstallation] box.
static String hetznerKey = 'hetznerKey';
/// A String field of [serverInstallation] box.
static String cloudFlareKey = 'cloudFlareKey';
/// A [User] field of [serverInstallation] box.
static String rootUser = 'rootUser';
/// A [ServerHostingDetails] field of [serverInstallation] box.
static String serverDetails = 'hetznerServer';
static String backblazeKey = 'backblazeKey';
/// A [BackblazeCredential] field of [serverInstallation] box.
static String backblazeCredential = 'backblazeKey';
/// A [BackblazeBucket] field of [serverInstallation] box.
static String backblazeBucket = 'backblazeBucket';
/// A boolean field of [serverInstallation] box.
static String isLoading = 'isLoading';
/// A boolean field of [serverInstallation] box.
static String isServerResetedFirstTime = 'isServerResetedFirstTime';
/// A boolean field of [serverInstallation] box.
static String isServerResetedSecondTime = 'isServerResetedSecondTime';
static String sshConfig = 'sshConfig';
static String sshPrivateKey = "sshPrivateKey";
static String sshPublicKey = "sshPublicKey";
/// Deprecated users box as it is unencrypted
static String usersDeprecated = 'users';
/// Box with users
static String users = 'usersEncrypted';
}

View File

@ -10,11 +10,12 @@ part 'authentication_dependend_state.dart';
abstract class ServerInstallationDependendCubit<
T extends ServerInstallationDependendState> extends Cubit<T> {
ServerInstallationDependendCubit(
this.appConfigCubit,
this.serverInstallationCubit,
T initState,
) : super(initState) {
authCubitSubscription = appConfigCubit.stream.listen(checkAuthStatus);
checkAuthStatus(appConfigCubit.state);
authCubitSubscription =
serverInstallationCubit.stream.listen(checkAuthStatus);
checkAuthStatus(serverInstallationCubit.state);
}
void checkAuthStatus(ServerInstallationState state) {
@ -26,7 +27,7 @@ abstract class ServerInstallationDependendCubit<
}
late StreamSubscription authCubitSubscription;
final ServerInstallationCubit appConfigCubit;
final ServerInstallationCubit serverInstallationCubit;
void load();
void clear();

View File

@ -9,22 +9,22 @@ part 'app_settings_state.dart';
class AppSettingsCubit extends Cubit<AppSettingsState> {
AppSettingsCubit({
required bool isDarkModeOn,
required bool isOnbordingShowing,
required bool isOnboardingShowing,
}) : super(
AppSettingsState(
isDarkModeOn: isDarkModeOn,
isOnbordingShowing: isOnbordingShowing,
isOnboardingShowing: isOnboardingShowing,
),
);
Box box = Hive.box(BNames.appSettings);
Box box = Hive.box(BNames.appSettingsBox);
void load() {
bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
bool? isOnbordingShowing = box.get(BNames.isOnbordingShowing);
bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing);
emit(state.copyWith(
isDarkModeOn: isDarkModeOn,
isOnbordingShowing: isOnbordingShowing,
isOnboardingShowing: isOnboardingShowing,
));
}
@ -34,8 +34,8 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
}
void turnOffOnboarding() {
box.put(BNames.isOnbordingShowing, false);
box.put(BNames.isOnboardingShowing, false);
emit(state.copyWith(isOnbordingShowing: false));
emit(state.copyWith(isOnboardingShowing: false));
}
}

View File

@ -3,18 +3,18 @@ part of 'app_settings_cubit.dart';
class AppSettingsState extends Equatable {
const AppSettingsState({
required this.isDarkModeOn,
required this.isOnbordingShowing,
required this.isOnboardingShowing,
});
final bool isDarkModeOn;
final bool isOnbordingShowing;
final bool isOnboardingShowing;
AppSettingsState copyWith({isDarkModeOn, isOnbordingShowing}) =>
AppSettingsState copyWith({isDarkModeOn, isOnboardingShowing}) =>
AppSettingsState(
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
isOnbordingShowing: isOnbordingShowing ?? this.isOnbordingShowing,
isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing,
);
@override
List<Object> get props => [isDarkModeOn, isOnbordingShowing];
List<Object> get props => [isDarkModeOn, isOnboardingShowing];
}

View File

@ -11,14 +11,14 @@ import 'package:selfprivacy/logic/models/json/backup.dart';
part 'backups_state.dart';
class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
BackupsCubit(ServerInstallationCubit appConfigCubit)
: super(appConfigCubit, BackupsState(preventActions: true));
BackupsCubit(ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, BackupsState(preventActions: true));
final api = ServerApi();
final backblaze = BackblazeApi();
Future<void> load() async {
if (appConfigCubit.state is ServerInstallationFinished) {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final bucket = getIt<ApiConfigModel>().backblazeBucket;
if (bucket == null) {
emit(BackupsState(
@ -85,9 +85,9 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
Future<void> createBucket() async {
emit(state.copyWith(preventActions: true));
final domain = appConfigCubit.state.serverDomain!.domainName
final domain = serverInstallationCubit.state.serverDomain!.domainName
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
final serverId = appConfigCubit.state.serverDetails!.id;
final serverId = serverInstallationCubit.state.serverDetails!.id;
var bucketName = 'selfprivacy-$domain-$serverId';
// If bucket name is too long, shorten it
if (bucketName.length > 49) {

View File

@ -10,8 +10,8 @@ part 'dns_records_state.dart';
class DnsRecordsCubit
extends ServerInstallationDependendCubit<DnsRecordsState> {
DnsRecordsCubit(ServerInstallationCubit appConfigCubit)
: super(appConfigCubit,
DnsRecordsCubit(ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit,
DnsRecordsState(dnsState: DnsRecordsStatus.refreshing));
final api = ServerApi();
@ -21,11 +21,12 @@ class DnsRecordsCubit
emit(DnsRecordsState(
dnsState: DnsRecordsStatus.refreshing,
dnsRecords: _getDesiredDnsRecords(
appConfigCubit.state.serverDomain?.domainName, "", "")));
serverInstallationCubit.state.serverDomain?.domainName, "", "")));
print('Loading DNS status');
if (appConfigCubit.state is ServerInstallationFinished) {
final ServerDomain? domain = appConfigCubit.state.serverDomain;
final String? ipAddress = appConfigCubit.state.serverDetails?.ip4;
if (serverInstallationCubit.state is ServerInstallationFinished) {
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress =
serverInstallationCubit.state.serverDetails?.ip4;
if (domain != null && ipAddress != null) {
final List<DnsRecord> records =
await cloudflare.getDnsRecords(cloudFlareDomain: domain);
@ -96,8 +97,8 @@ class DnsRecordsCubit
Future<void> fix() async {
emit(state.copyWith(dnsState: DnsRecordsStatus.refreshing));
final ServerDomain? domain = appConfigCubit.state.serverDomain;
final String? ipAddress = appConfigCubit.state.serverDetails?.ip4;
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4;
final String? dkimPublicKey = await api.getDkim();
await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!);
await cloudflare.createMultipleDnsRecords(

View File

@ -2,8 +2,6 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/get_it/ssh.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
@ -18,7 +16,7 @@ part '../server_installation/server_installation_state.dart';
class ServerInstallationCubit extends Cubit<ServerInstallationState> {
ServerInstallationCubit() : super(ServerInstallationEmpty());
final repository = AppConfigRepository();
final repository = ServerInstallationRepository();
Timer? timer;
@ -266,7 +264,6 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
if (state.serverDetails != null) {
await repository.deleteServer(state.serverDomain!);
await getIt<SSHModel>().clear();
}
await repository.deleteRecords();
emit(ServerInstallationNotFinished(

View File

@ -17,8 +17,8 @@ import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
import '../server_installation/server_installation_cubit.dart';
class AppConfigRepository {
Box box = Hive.box(BNames.appConfig);
class ServerInstallationRepository {
Box box = Hive.box(BNames.serverInstallation);
Future<ServerInstallationState> load() async {
final hetznerToken = getIt<ApiConfigModel>().hetznerKey;

View File

@ -1,5 +1,3 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
@ -7,13 +5,11 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
part 'services_state.dart';
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
ServicesCubit(ServerInstallationCubit appConfigCubit)
: super(appConfigCubit, ServicesState.allOff());
Box box = Hive.box(BNames.servicesState);
ServicesCubit(ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, ServicesState.allOff());
final api = ServerApi();
Future<void> load() async {
if (appConfigCubit.state is ServerInstallationFinished) {
if (serverInstallationCubit.state is ServerInstallationFinished) {
var statuses = await api.servicesPowerCheck();
emit(
ServicesState(
@ -29,7 +25,6 @@ class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
@override
void clear() async {
box.clear();
emit(ServicesState.allOff());
}
}

View File

@ -1,4 +1,3 @@
import 'package:bloc/bloc.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
@ -11,23 +10,23 @@ export 'package:provider/provider.dart';
part 'users_state.dart';
class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
UsersCubit(ServerInstallationCubit appConfigCubit)
UsersCubit(ServerInstallationCubit serverInstallationCubit)
: super(
appConfigCubit,
serverInstallationCubit,
UsersState(
<User>[], User(login: 'root'), User(login: 'loading...')));
Box<User> box = Hive.box<User>(BNames.users);
Box configBox = Hive.box(BNames.appConfig);
Box serverInstallationBox = Hive.box(BNames.serverInstallation);
final api = ServerApi();
Future<void> load() async {
if (appConfigCubit.state is ServerInstallationFinished) {
if (serverInstallationCubit.state is ServerInstallationFinished) {
var loadedUsers = box.values.toList();
final primaryUser = configBox.get(BNames.rootUser,
final primaryUser = serverInstallationBox.get(BNames.rootUser,
defaultValue: User(login: 'loading...'));
List<String> rootKeys = [
...configBox.get(BNames.rootKeys, defaultValue: [])
...serverInstallationBox.get(BNames.rootKeys, defaultValue: [])
];
if (loadedUsers.isNotEmpty) {
emit(UsersState(
@ -48,10 +47,10 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
box.addAll(usersWithSshKeys);
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
configBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
configBox.put(BNames.rootUser, primaryUserWithSshKeys);
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit(UsersState(
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
}
@ -142,10 +141,10 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
box.clear();
box.addAll(usersWithSshKeys);
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
configBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
configBox.put(BNames.rootUser, primaryUserWithSshKeys);
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit(UsersState(
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
return;
@ -195,10 +194,10 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
final result = await api.addRootSshKey(publicKey);
if (result.isSuccess) {
// Add ssh key to the array of root keys
final rootKeys =
configBox.get(BNames.rootKeys, defaultValue: []) as List<String>;
final rootKeys = serverInstallationBox
.get(BNames.rootKeys, defaultValue: []) as List<String>;
rootKeys.add(publicKey);
configBox.put(BNames.rootKeys, rootKeys);
serverInstallationBox.put(BNames.rootKeys, rootKeys);
emit(state.copyWith(
rootUser: User(
login: state.rootUser.login,
@ -224,7 +223,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
configBox.put(BNames.rootUser, updatedUser);
serverInstallationBox.put(BNames.rootUser, updatedUser);
emit(state.copyWith(
primaryUser: updatedUser,
));
@ -258,10 +257,10 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
// If it is not primary user, update user
if (user.login == 'root') {
final rootKeys =
configBox.get(BNames.rootKeys, defaultValue: []) as List<String>;
final rootKeys = serverInstallationBox
.get(BNames.rootKeys, defaultValue: []) as List<String>;
rootKeys.remove(publicKey);
configBox.put(BNames.rootKeys, rootKeys);
serverInstallationBox.put(BNames.rootKeys, rootKeys);
emit(state.copyWith(
rootUser: User(
login: state.rootUser.login,
@ -284,7 +283,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
configBox.put(BNames.rootUser, updatedUser);
serverInstallationBox.put(BNames.rootUser, updatedUser);
emit(state.copyWith(
primaryUser: updatedUser,
));

View File

@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
class ApiConfigModel {
Box _box = Hive.box(BNames.appConfig);
Box _box = Hive.box(BNames.serverInstallation);
ServerHostingDetails? get serverDetails => _serverDetails;
String? get hetznerKey => _hetznerKey;
@ -33,7 +33,7 @@ class ApiConfigModel {
}
Future<void> storeBackblazeCredential(BackblazeCredential value) async {
await _box.put(BNames.backblazeKey, value);
await _box.put(BNames.backblazeCredential, value);
_backblazeCredential = value;
}
@ -66,7 +66,7 @@ class ApiConfigModel {
_hetznerKey = _box.get(BNames.hetznerKey);
_cloudFlareKey = _box.get(BNames.cloudFlareKey);
_backblazeCredential = _box.get(BNames.backblazeKey);
_backblazeCredential = _box.get(BNames.backblazeCredential);
_serverDomain = _box.get(BNames.serverDomain);
_serverDetails = _box.get(BNames.serverDetails);
_backblazeBucket = _box.get(BNames.backblazeBucket);

View File

@ -1,39 +0,0 @@
import 'package:hive/hive.dart';
import 'package:pointycastle/pointycastle.dart';
import 'package:rsa_encrypt/rsa_encrypt.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:pointycastle/api.dart' as crypto;
import 'package:ssh_key/ssh_key.dart' as ssh_key;
class SSHModel {
Box _box = Hive.box(BNames.sshConfig);
String? savedPrivateKey;
String? savedPubKey;
Future<void> generateKeys() async {
var helper = RsaKeyHelper();
crypto.AsymmetricKeyPair pair =
await helper.computeRSAKeyPair(helper.getSecureRandom());
var privateKey = pair.privateKey as RSAPrivateKey;
var publicKey = pair.publicKey as RSAPublicKey;
savedPrivateKey = helper.encodePrivateKeyToPemPKCS1(privateKey);
savedPubKey = publicKey.encode(ssh_key.PubKeyEncoding.openSsh);
await _box.put(BNames.sshPrivateKey, savedPrivateKey);
await _box.put(BNames.sshPublicKey, savedPubKey);
}
void init() async {
savedPrivateKey = _box.get(BNames.sshPrivateKey);
savedPubKey = _box.get(BNames.sshPublicKey);
}
bool get isSSHKeyGenerated => savedPrivateKey != null && savedPubKey != null;
Future<void> clear() async {
savedPrivateKey = null;
savedPubKey = null;
await _box.clear();
}
}

View File

@ -1,4 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:json_annotation/json_annotation.dart';
part 'api_token.g.dart';

View File

@ -83,7 +83,7 @@ class MyApp extends StatelessWidget {
darkTheme: darkThemeData,
themeMode:
appSettings.isDarkModeOn ? ThemeMode.dark : ThemeMode.light,
home: appSettings.isOnbordingShowing
home: appSettings.isOnboardingShowing
? OnboardingPage(nextPage: InitializingPage())
: RootPage(),
builder: (BuildContext context, Widget? widget) {

View File

@ -4,7 +4,7 @@ import 'package:flutter/services.dart' show rootBundle;
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
class BrandMarkdown extends StatefulWidget {
const BrandMarkdown({
@ -60,9 +60,9 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
styleSheet: markdown,
onTapLink: (String text, String? href, String title) {
if (href != null) {
canLaunch(href).then((canLaunchURL) {
canLaunchUrlString(href).then((canLaunchURL) {
if (canLaunchURL) {
launch(href);
launchUrlString(href);
}
});
}

View File

@ -26,8 +26,8 @@ class BrandSpanButton extends TextSpan {
);
static _launchURL(String link) async {
if (await canLaunch(link)) {
await launch(link);
if (await canLaunchUrl(Uri.parse(link))) {
await launchUrl(Uri.parse(link));
} else {
throw 'Could not launch $link';
}

View File

@ -19,7 +19,7 @@ import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/utils/ui_helpers.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import '../rootRoute.dart';
@ -39,13 +39,12 @@ class ServicesPage extends StatefulWidget {
}
void _launchURL(url) async {
var _possible = await canLaunch(url);
var _possible = await canLaunchUrlString(url);
if (_possible) {
try {
await launch(
await launchUrlString(
url,
enableJavaScript: true,
);
} catch (e) {
print(e);
@ -151,11 +150,9 @@ class _Card extends StatelessWidget {
builder: (context) {
late bool isActive;
if (hasSwitchJob) {
isActive = ((jobState as JobsStateWithJobs)
.jobList
.firstWhere((el) =>
el is ServiceToggleJob &&
el.type == serviceType) as ServiceToggleJob)
isActive = ((jobState).jobList.firstWhere((el) =>
el is ServiceToggleJob &&
el.type == serviceType) as ServiceToggleJob)
.needToTurnOn;
} else {
isActive = serviceState.isEnableByType(serviceType);

View File

@ -10,11 +10,11 @@ import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.da
class RecoveryDomain extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appConfig = context.watch<ServerInstallationCubit>();
var serverInstallation = context.watch<ServerInstallationCubit>();
return BlocProvider(
create: (context) =>
RecoveryDomainFormCubit(appConfig, FieldCubitFactory(context)),
create: (context) => RecoveryDomainFormCubit(
serverInstallation, FieldCubitFactory(context)),
child: Builder(
builder: (context) {
var formCubitState = context.watch<RecoveryDomainFormCubit>().state;

View File

@ -715,7 +715,7 @@ packages:
source: hosted
version: "2.1.2"
pointycastle:
dependency: "direct main"
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
@ -770,13 +770,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
rsa_encrypt:
dependency: "direct main"
description:
name: rsa_encrypt
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
share_plus:
dependency: "direct main"
description:

View File

@ -34,10 +34,8 @@ dependencies:
modal_bottom_sheet: ^2.0.1
nanoid: ^1.0.0
package_info: ^2.0.2
pointycastle: ^3.5.1
pretty_dio_logger: ^1.2.0-beta-1
provider: ^6.0.2
rsa_encrypt: ^2.0.0
share_plus: ^4.0.4
ssh_key: ^0.7.1
system_theme: ^2.0.0