mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-03-11 09:14:10 +00:00
feat: Allow setting the provider token after recovery
This commit is contained in:
parent
51b6e0ab41
commit
efed52f3ec
14 changed files with 512 additions and 40 deletions
assets/translations
lib
logic
bloc
cubit
forms/setup/initializing
server_installation
get_it
models/hive
ui
pages
more/tokens
setup/recovering
router
|
@ -693,6 +693,11 @@
|
||||||
"loading": "Loading token status",
|
"loading": "Loading token status",
|
||||||
"used_by": "Used for {servers}.",
|
"used_by": "Used for {servers}.",
|
||||||
"check_again": "Check again",
|
"check_again": "Check again",
|
||||||
"no_tokens": "No tokens"
|
"no_tokens": "No tokens",
|
||||||
|
"server_without_token": "{server_domain} has no token for {provider}.",
|
||||||
|
"tap_to_add_token": "Tap here to add a token",
|
||||||
|
"add_server_provider_token": "Add server provider token",
|
||||||
|
"server_provider_unknown": "Unknown server provider",
|
||||||
|
"server_provider_unknown_description": "Your server provider is not supported by this app version."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ import 'package:selfprivacy/logic/get_it/resources_model.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server.dart';
|
import 'package:selfprivacy/logic/models/hive/server.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/logic/providers/backups_providers/backups_provider_factory.dart';
|
import 'package:selfprivacy/logic/providers/backups_providers/backups_provider_factory.dart';
|
||||||
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider_factory.dart';
|
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider_factory.dart';
|
||||||
import 'package:selfprivacy/logic/providers/provider_settings.dart';
|
import 'package:selfprivacy/logic/providers/provider_settings.dart';
|
||||||
|
import 'package:selfprivacy/logic/providers/providers_controller.dart';
|
||||||
import 'package:selfprivacy/logic/providers/server_providers/server_provider_factory.dart';
|
import 'package:selfprivacy/logic/providers/server_providers/server_provider_factory.dart';
|
||||||
|
|
||||||
part 'tokens_event.dart';
|
part 'tokens_event.dart';
|
||||||
|
@ -23,6 +26,12 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
|
||||||
validateTokens,
|
validateTokens,
|
||||||
transformer: droppable(),
|
transformer: droppable(),
|
||||||
);
|
);
|
||||||
|
on<AddServerProviderToken>(
|
||||||
|
addServerProviderCredential,
|
||||||
|
);
|
||||||
|
on<ServerSelectedForProviderToken>(
|
||||||
|
connectServerToProviderToken,
|
||||||
|
);
|
||||||
|
|
||||||
add(const RevalidateTokens());
|
add(const RevalidateTokens());
|
||||||
|
|
||||||
|
@ -154,6 +163,51 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
|
||||||
return TokenStatus.valid;
|
return TokenStatus.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addServerProviderCredential(
|
||||||
|
final AddServerProviderToken event,
|
||||||
|
final Emitter<TokensState> emit,
|
||||||
|
) async {
|
||||||
|
await getIt<ResourcesModel>()
|
||||||
|
.addServerProviderToken(event.serverProviderCredential);
|
||||||
|
|
||||||
|
final ServerProviderSettings settings = ServerProviderSettings(
|
||||||
|
provider: event.serverProviderCredential.provider,
|
||||||
|
token: event.serverProviderCredential.token,
|
||||||
|
isAuthorized: true,
|
||||||
|
);
|
||||||
|
ProvidersController.initServerProvider(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> connectServerToProviderToken(
|
||||||
|
final ServerSelectedForProviderToken event,
|
||||||
|
final Emitter<TokensState> emit,
|
||||||
|
) async {
|
||||||
|
await getIt<ResourcesModel>().associateServerWithToken(
|
||||||
|
event.providerServer.id,
|
||||||
|
event.serverProviderCredential.token,
|
||||||
|
);
|
||||||
|
final Server newServerData = Server(
|
||||||
|
domain: event.server.domain,
|
||||||
|
hostingDetails: ServerHostingDetails(
|
||||||
|
ip4: event.providerServer.ip,
|
||||||
|
id: event.providerServer.id,
|
||||||
|
createTime: event.providerServer.created,
|
||||||
|
volume: ServerProviderVolume(
|
||||||
|
id: 0,
|
||||||
|
name: 'recovered_volume',
|
||||||
|
sizeByte: 0,
|
||||||
|
serverId: event.providerServer.id,
|
||||||
|
linuxDevice: '',
|
||||||
|
),
|
||||||
|
apiToken: event.server.hostingDetails.apiToken,
|
||||||
|
provider: event.serverProviderCredential.provider,
|
||||||
|
serverLocation: event.server.hostingDetails.serverLocation,
|
||||||
|
serverType: event.server.hostingDetails.serverType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await getIt<ResourcesModel>().updateServerByDomain(newServerData);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_resourcesModelSubscription.cancel();
|
_resourcesModelSubscription.cancel();
|
||||||
|
|
|
@ -10,3 +10,27 @@ class RevalidateTokens extends TokensEvent {
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AddServerProviderToken extends TokensEvent {
|
||||||
|
const AddServerProviderToken(this.serverProviderCredential);
|
||||||
|
|
||||||
|
final ServerProviderCredential serverProviderCredential;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [serverProviderCredential];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerSelectedForProviderToken extends TokensEvent {
|
||||||
|
const ServerSelectedForProviderToken(
|
||||||
|
this.providerServer,
|
||||||
|
this.server,
|
||||||
|
this.serverProviderCredential,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ServerBasicInfoWithValidators providerServer;
|
||||||
|
final Server server;
|
||||||
|
final ServerProviderCredential serverProviderCredential;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [providerServer, server, serverProviderCredential];
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,21 @@ sealed class TokensState extends Equatable {
|
||||||
List<TokenStatusWrapper<BackupsCredential>> get backupsCredentials;
|
List<TokenStatusWrapper<BackupsCredential>> get backupsCredentials;
|
||||||
List<Server> get servers => _servers;
|
List<Server> get servers => _servers;
|
||||||
|
|
||||||
|
List<Server> get serversWithoutProviderCredentials => servers
|
||||||
|
.where(
|
||||||
|
(final Server server) =>
|
||||||
|
server.hostingDetails.provider != ServerProviderType.unknown &&
|
||||||
|
serverProviderCredentials.every(
|
||||||
|
(
|
||||||
|
final TokenStatusWrapper<ServerProviderCredential>
|
||||||
|
serverProviderCredential,
|
||||||
|
) =>
|
||||||
|
!serverProviderCredential.data.associatedServerIds
|
||||||
|
.contains(server.hostingDetails.id),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
Server getServerById(final int serverId) => servers.firstWhere(
|
Server getServerById(final int serverId) => servers.firstWhere(
|
||||||
(final Server server) => server.hostingDetails.id == serverId,
|
(final Server server) => server.hostingDetails.id == serverId,
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:equatable/equatable.dart';
|
||||||
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/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||||
|
import 'package:selfprivacy/logic/get_it/resources_model.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
@ -68,10 +69,18 @@ class VolumesBloc extends Bloc<VolumesEvent, VolumesState> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_resourcesModelSubscription =
|
||||||
|
getIt<ResourcesModel>().statusStream.listen((final event) {
|
||||||
|
if (event is ChangedServerProviderCredentials) {
|
||||||
|
add(const VolumesServerLoaded());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
late StreamSubscription _apiStatusSubscription;
|
late StreamSubscription _apiStatusSubscription;
|
||||||
late StreamSubscription _apiDataSubscription;
|
late StreamSubscription _apiDataSubscription;
|
||||||
|
late StreamSubscription _resourcesModelSubscription;
|
||||||
bool isLoaded = false;
|
bool isLoaded = false;
|
||||||
|
|
||||||
Future<Price?> getPricePerGb() async {
|
Future<Price?> getPricePerGb() async {
|
||||||
|
@ -145,6 +154,7 @@ class VolumesBloc extends Bloc<VolumesEvent, VolumesState> {
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await _apiStatusSubscription.cancel();
|
await _apiStatusSubscription.cancel();
|
||||||
await _apiDataSubscription.cancel();
|
await _apiDataSubscription.cancel();
|
||||||
|
await _resourcesModelSubscription.cancel();
|
||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
|
||||||
|
class AddServerProviderToExistingServerFormCubit extends FormCubit {
|
||||||
|
AddServerProviderToExistingServerFormCubit(
|
||||||
|
this.serverInstallationCubit,
|
||||||
|
this.setServerProviderKey,
|
||||||
|
) {
|
||||||
|
apiKey = FieldCubit(
|
||||||
|
initalValue: '',
|
||||||
|
validations: [
|
||||||
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
super.addFields([apiKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<void> onSubmit() async {
|
||||||
|
// serverInstallationCubit.setServerProviderKey(apiKey.state.value);
|
||||||
|
setServerProviderKey(apiKey.state.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
final Function(String) setServerProviderKey;
|
||||||
|
late final FieldCubit<String> apiKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<bool> asyncValidation() async {
|
||||||
|
bool? isKeyValid;
|
||||||
|
|
||||||
|
try {
|
||||||
|
isKeyValid = await serverInstallationCubit
|
||||||
|
.isServerProviderApiTokenValid(apiKey.state.value);
|
||||||
|
} catch (e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isKeyValid == null) {
|
||||||
|
apiKey.setError('');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isKeyValid) {
|
||||||
|
apiKey.setError('initializing.provider_bad_key_error'.tr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return isKeyValid;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
@ -708,29 +709,54 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfoWithValidators>> getAvailableServers() async {
|
Future<List<ServerBasicInfoWithValidators>> getAvailableServers({
|
||||||
final ServerInstallationRecovery dataState =
|
final Server? server,
|
||||||
state as ServerInstallationRecovery;
|
}) async {
|
||||||
final List<ServerBasicInfo> servers =
|
|
||||||
await repository.getServersOnProviderAccount();
|
|
||||||
List<ServerBasicInfoWithValidators> validatedList = [];
|
List<ServerBasicInfoWithValidators> validatedList = [];
|
||||||
try {
|
if (server != null) {
|
||||||
final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
|
final List<ServerBasicInfo> servers =
|
||||||
(final ServerBasicInfo server) =>
|
await repository.getServersOnProviderAccount();
|
||||||
ServerBasicInfoWithValidators.fromServerBasicInfo(
|
try {
|
||||||
serverBasicInfo: server,
|
final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
|
||||||
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
(final ServerBasicInfo hostingServer) =>
|
||||||
isReverseDnsValid:
|
ServerBasicInfoWithValidators.fromServerBasicInfo(
|
||||||
server.reverseDns == dataState.serverDomain?.domainName ||
|
serverBasicInfo: hostingServer,
|
||||||
server.reverseDns ==
|
isIpValid: hostingServer.ip == server.hostingDetails.ip4,
|
||||||
dataState.serverDomain?.domainName.split('.')[0],
|
isReverseDnsValid:
|
||||||
),
|
hostingServer.reverseDns == server.domain.domainName ||
|
||||||
);
|
hostingServer.reverseDns ==
|
||||||
validatedList = validated.toList();
|
server.domain.domainName.split('.')[0],
|
||||||
} catch (e) {
|
),
|
||||||
print(e);
|
);
|
||||||
getIt<NavigationService>()
|
validatedList = validated.toList();
|
||||||
.showSnackBar('modals.server_validators_error'.tr());
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
getIt<NavigationService>()
|
||||||
|
.showSnackBar('modals.server_validators_error'.tr());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final ServerInstallationRecovery dataState =
|
||||||
|
state as ServerInstallationRecovery;
|
||||||
|
final List<ServerBasicInfo> servers =
|
||||||
|
await repository.getServersOnProviderAccount();
|
||||||
|
try {
|
||||||
|
final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
|
||||||
|
(final ServerBasicInfo server) =>
|
||||||
|
ServerBasicInfoWithValidators.fromServerBasicInfo(
|
||||||
|
serverBasicInfo: server,
|
||||||
|
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
||||||
|
isReverseDnsValid:
|
||||||
|
server.reverseDns == dataState.serverDomain?.domainName ||
|
||||||
|
server.reverseDns ==
|
||||||
|
dataState.serverDomain?.domainName.split('.')[0],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
validatedList = validated.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
getIt<NavigationService>()
|
||||||
|
.showSnackBar('modals.server_validators_error'.tr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return validatedList;
|
return validatedList;
|
||||||
|
|
|
@ -173,6 +173,17 @@ class ResourcesModel {
|
||||||
_statusStreamController.add(const ChangedServers());
|
_statusStreamController.add(const ChangedServers());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateServerByDomain(final Server server) async {
|
||||||
|
final index = _servers.indexWhere(
|
||||||
|
(final s) => s.domain.domainName == server.domain.domainName,
|
||||||
|
);
|
||||||
|
if (index != -1) {
|
||||||
|
_servers[index] = server;
|
||||||
|
await _box.put(BNames.servers, _servers);
|
||||||
|
_statusStreamController.add(const ChangedServers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> setBackblazeBucket(final BackblazeBucket bucket) async {
|
Future<void> setBackblazeBucket(final BackblazeBucket bucket) async {
|
||||||
_backblazeBucket = bucket;
|
_backblazeBucket = bucket;
|
||||||
await _box.put(BNames.backblazeBucket, _backblazeBucket);
|
await _box.put(BNames.backblazeBucket, _backblazeBucket);
|
||||||
|
|
|
@ -117,6 +117,12 @@ enum ServerProviderType {
|
||||||
hetzner => 'Hetzner Cloud',
|
hetzner => 'Hetzner Cloud',
|
||||||
unknown => 'Unknown',
|
unknown => 'Unknown',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
String get supportArticle => switch (this) {
|
||||||
|
digitalOcean => 'how_digital_ocean',
|
||||||
|
hetzner => 'how_hetzner',
|
||||||
|
unknown => '',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ServerProviderTypeIsSpecified on ServerProviderType? {
|
extension ServerProviderTypeIsSpecified on ServerProviderType? {
|
||||||
|
|
167
lib/ui/pages/more/tokens/add_server_provider_token.dart
Normal file
167
lib/ui/pages/more/tokens/add_server_provider_token.dart
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:selfprivacy/logic/bloc/tokens/tokens_bloc.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/add_server_provider_to_exsisting_server_form_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||||
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class AddServerProviderTokenPage extends StatefulWidget {
|
||||||
|
const AddServerProviderTokenPage({
|
||||||
|
required this.server,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Server server;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AddServerProviderTokenPage> createState() =>
|
||||||
|
_AddServerProviderTokenPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddServerProviderTokenPageState
|
||||||
|
extends State<AddServerProviderTokenPage> {
|
||||||
|
bool isChoosingServer = false;
|
||||||
|
ServerProviderCredential? credential;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
final ServerInstallationCubit appConfig =
|
||||||
|
context.watch<ServerInstallationCubit>();
|
||||||
|
|
||||||
|
void setServerProviderKey(final String key) {
|
||||||
|
final newCredential = ServerProviderCredential(
|
||||||
|
token: key,
|
||||||
|
provider: widget.server.hostingDetails.provider,
|
||||||
|
tokenId: null,
|
||||||
|
associatedServerIds: [],
|
||||||
|
);
|
||||||
|
context.read<TokensBloc>().add(
|
||||||
|
AddServerProviderToken(newCredential),
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
isChoosingServer = true;
|
||||||
|
credential = newCredential;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChoosingServer && credential != null) {
|
||||||
|
return RecoveryConfirmServer(
|
||||||
|
server: widget.server,
|
||||||
|
serverProviderCredential: credential,
|
||||||
|
submitCallback: () {
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.server.hostingDetails.provider == ServerProviderType.unknown) {
|
||||||
|
return BrandHeroScreen(
|
||||||
|
heroTitle: 'tokens.server_provider_unknown'.tr(),
|
||||||
|
heroSubtitle: 'tokens.server_provider_unknown_description'.tr(),
|
||||||
|
hasBackButton: true,
|
||||||
|
hasFlashButton: false,
|
||||||
|
ignoreBreakpoints: true,
|
||||||
|
onBackButtonPressed: () {
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst);
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
BrandButton.filled(
|
||||||
|
text: 'basis.close'.tr(),
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _TokenProviderInput(
|
||||||
|
appConfig: appConfig,
|
||||||
|
server: widget.server,
|
||||||
|
setServerProviderKey: setServerProviderKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TokenProviderInput extends StatelessWidget {
|
||||||
|
const _TokenProviderInput({
|
||||||
|
required this.appConfig,
|
||||||
|
required this.server,
|
||||||
|
required this.setServerProviderKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ServerInstallationCubit appConfig;
|
||||||
|
final Server server;
|
||||||
|
final Function(String) setServerProviderKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => BlocProvider(
|
||||||
|
create: (final BuildContext context) =>
|
||||||
|
AddServerProviderToExistingServerFormCubit(
|
||||||
|
appConfig,
|
||||||
|
setServerProviderKey,
|
||||||
|
),
|
||||||
|
child: Builder(
|
||||||
|
builder: (final BuildContext context) => BrandHeroScreen(
|
||||||
|
heroTitle: 'recovering.provider_connected'.tr(
|
||||||
|
args: [
|
||||||
|
server.hostingDetails.provider.displayName,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
heroSubtitle: 'recovering.provider_connected_description'.tr(
|
||||||
|
args: [server.domain.domainName],
|
||||||
|
),
|
||||||
|
hasBackButton: true,
|
||||||
|
hasFlashButton: false,
|
||||||
|
hasSupportDrawer: true,
|
||||||
|
onBackButtonPressed: () {
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst);
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
CubitFormTextField(
|
||||||
|
autofocus: true,
|
||||||
|
formFieldCubit: context
|
||||||
|
.read<AddServerProviderToExistingServerFormCubit>()
|
||||||
|
.apiKey,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
labelText: 'recovering.provider_connected_placeholder'.tr(
|
||||||
|
args: [
|
||||||
|
server.hostingDetails.provider.displayName,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
BrandButton.filled(
|
||||||
|
onPressed: () => context
|
||||||
|
.read<AddServerProviderToExistingServerFormCubit>()
|
||||||
|
.trySubmit(),
|
||||||
|
child: Text('basis.continue'.tr()),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Builder(
|
||||||
|
builder: (final context) => BrandButton.text(
|
||||||
|
title: 'initializing.how'.tr(),
|
||||||
|
onPressed: () => context
|
||||||
|
.read<SupportSystemCubit>()
|
||||||
|
.showArticle(
|
||||||
|
article: server.hostingDetails.provider.supportArticle,
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
|
||||||
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
||||||
import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart';
|
import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class TokensPage extends StatelessWidget {
|
class TokensPage extends StatelessWidget {
|
||||||
|
@ -29,10 +30,7 @@ class TokensPage extends StatelessWidget {
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.server_provider_tokens'.tr(),
|
title: 'tokens.server_provider_tokens'.tr(),
|
||||||
),
|
),
|
||||||
Divider(
|
const Divider(height: 0),
|
||||||
height: 0,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
if (state.serverProviderCredentials.isEmpty)
|
if (state.serverProviderCredentials.isEmpty)
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.no_tokens'.tr(),
|
title: 'tokens.no_tokens'.tr(),
|
||||||
|
@ -48,6 +46,34 @@ class TokensPage extends StatelessWidget {
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
if (state.serversWithoutProviderCredentials.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const Divider(height: 0),
|
||||||
|
Column(
|
||||||
|
children: state.serversWithoutProviderCredentials
|
||||||
|
.map(
|
||||||
|
(final server) => ListTileOnSurfaceVariant(
|
||||||
|
title: 'tokens.server_without_token'.tr(
|
||||||
|
namedArgs: {
|
||||||
|
'server_domain': server.domain.domainName,
|
||||||
|
'provider': server
|
||||||
|
.hostingDetails.provider.displayName,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
subtitle: 'tokens.tap_to_add_token'.tr(),
|
||||||
|
leadingIcon: Icons.add_circle_outline,
|
||||||
|
onTap: () => context.router.push(
|
||||||
|
AddServerProviderTokenRoute(
|
||||||
|
server: server,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -58,10 +84,7 @@ class TokensPage extends StatelessWidget {
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.dns_provider_tokens'.tr(),
|
title: 'tokens.dns_provider_tokens'.tr(),
|
||||||
),
|
),
|
||||||
Divider(
|
const Divider(height: 0),
|
||||||
height: 0,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
if (state.dnsProviderCredentials.isEmpty)
|
if (state.dnsProviderCredentials.isEmpty)
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.no_tokens'.tr(),
|
title: 'tokens.no_tokens'.tr(),
|
||||||
|
@ -86,10 +109,7 @@ class TokensPage extends StatelessWidget {
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.backup_provider_tokens'.tr(),
|
title: 'tokens.backup_provider_tokens'.tr(),
|
||||||
),
|
),
|
||||||
Divider(
|
const Divider(height: 0),
|
||||||
height: 0,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
if (state.backupsCredentials.isEmpty)
|
if (state.backupsCredentials.isEmpty)
|
||||||
ListTileOnSurfaceVariant(
|
ListTileOnSurfaceVariant(
|
||||||
title: 'tokens.no_tokens'.tr(),
|
title: 'tokens.no_tokens'.tr(),
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/logic/bloc/tokens/tokens_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
|
||||||
class RecoveryConfirmServer extends StatefulWidget {
|
class RecoveryConfirmServer extends StatefulWidget {
|
||||||
const RecoveryConfirmServer({super.key});
|
const RecoveryConfirmServer({
|
||||||
|
this.server,
|
||||||
|
this.serverProviderCredential,
|
||||||
|
this.submitCallback,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Server? server;
|
||||||
|
final ServerProviderCredential? serverProviderCredential;
|
||||||
|
final Function? submitCallback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RecoveryConfirmServer> createState() => _RecoveryConfirmServerState();
|
State<RecoveryConfirmServer> createState() => _RecoveryConfirmServerState();
|
||||||
|
@ -45,8 +57,9 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
||||||
future:
|
future: context
|
||||||
context.read<ServerInstallationCubit>().getAvailableServers(),
|
.read<ServerInstallationCubit>()
|
||||||
|
.getAvailableServers(server: widget.server),
|
||||||
builder: (final context, final snapshot) {
|
builder: (final context, final snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final servers = snapshot.data;
|
final servers = snapshot.data;
|
||||||
|
@ -248,8 +261,21 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('modals.yes'.tr()),
|
child: Text('modals.yes'.tr()),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<ServerInstallationCubit>().setServerId(server);
|
if (widget.server != null &&
|
||||||
Navigator.of(context).pop();
|
widget.serverProviderCredential != null) {
|
||||||
|
context.read<TokensBloc>().add(
|
||||||
|
ServerSelectedForProviderToken(
|
||||||
|
server,
|
||||||
|
widget.server!,
|
||||||
|
widget.serverProviderCredential!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
widget.submitCallback?.call();
|
||||||
|
} else {
|
||||||
|
context.read<ServerInstallationCubit>().setServerId(server);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:animations/animations.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server.dart';
|
||||||
import 'package:selfprivacy/logic/models/service.dart';
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
import 'package:selfprivacy/ui/pages/backups/backup_details.dart';
|
import 'package:selfprivacy/ui/pages/backups/backup_details.dart';
|
||||||
import 'package:selfprivacy/ui/pages/backups/backups_list.dart';
|
import 'package:selfprivacy/ui/pages/backups/backups_list.dart';
|
||||||
|
@ -12,6 +13,7 @@ import 'package:selfprivacy/ui/pages/more/app_settings/app_settings.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/app_settings/developer_settings.dart';
|
import 'package:selfprivacy/ui/pages/more/app_settings/developer_settings.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/console/console_page.dart';
|
import 'package:selfprivacy/ui/pages/more/console/console_page.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/more.dart';
|
import 'package:selfprivacy/ui/pages/more/more.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/more/tokens/add_server_provider_token.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/tokens/tokens_page.dart';
|
import 'package:selfprivacy/ui/pages/more/tokens/tokens_page.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||||
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
||||||
|
@ -111,6 +113,7 @@ class RootRouter extends _$RootRouter {
|
||||||
AutoRoute(page: ServerLogsRoute.page),
|
AutoRoute(page: ServerLogsRoute.page),
|
||||||
AutoRoute(page: TokensRoute.page),
|
AutoRoute(page: TokensRoute.page),
|
||||||
AutoRoute(page: MemoryUsageByServiceRoute.page),
|
AutoRoute(page: MemoryUsageByServiceRoute.page),
|
||||||
|
AutoRoute(page: AddServerProviderTokenRoute.page),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AutoRoute(page: ServicesMigrationRoute.page),
|
AutoRoute(page: ServicesMigrationRoute.page),
|
||||||
|
@ -170,6 +173,8 @@ String getRouteTitle(final String routeName) {
|
||||||
return 'tokens.title';
|
return 'tokens.title';
|
||||||
case 'MemoryUsageByServiceRoute':
|
case 'MemoryUsageByServiceRoute':
|
||||||
return 'resource_chart.memory';
|
return 'resource_chart.memory';
|
||||||
|
case 'AddServerProviderTokenPage':
|
||||||
|
return 'tokens.add_server_provider_token';
|
||||||
default:
|
default:
|
||||||
return routeName;
|
return routeName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,16 @@ abstract class _$RootRouter extends RootStackRouter {
|
||||||
child: const AboutApplicationPage(),
|
child: const AboutApplicationPage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
AddServerProviderTokenRoute.name: (routeData) {
|
||||||
|
final args = routeData.argsAs<AddServerProviderTokenRouteArgs>();
|
||||||
|
return AutoRoutePage<dynamic>(
|
||||||
|
routeData: routeData,
|
||||||
|
child: AddServerProviderTokenPage(
|
||||||
|
server: args.server,
|
||||||
|
key: args.key,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
AppSettingsRoute.name: (routeData) {
|
AppSettingsRoute.name: (routeData) {
|
||||||
return AutoRoutePage<dynamic>(
|
return AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
|
@ -242,6 +252,45 @@ class AboutApplicationRoute extends PageRouteInfo<void> {
|
||||||
static const PageInfo<void> page = PageInfo<void>(name);
|
static const PageInfo<void> page = PageInfo<void>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [AddServerProviderTokenPage]
|
||||||
|
class AddServerProviderTokenRoute
|
||||||
|
extends PageRouteInfo<AddServerProviderTokenRouteArgs> {
|
||||||
|
AddServerProviderTokenRoute({
|
||||||
|
required Server server,
|
||||||
|
Key? key,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
AddServerProviderTokenRoute.name,
|
||||||
|
args: AddServerProviderTokenRouteArgs(
|
||||||
|
server: server,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'AddServerProviderTokenRoute';
|
||||||
|
|
||||||
|
static const PageInfo<AddServerProviderTokenRouteArgs> page =
|
||||||
|
PageInfo<AddServerProviderTokenRouteArgs>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddServerProviderTokenRouteArgs {
|
||||||
|
const AddServerProviderTokenRouteArgs({
|
||||||
|
required this.server,
|
||||||
|
this.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Server server;
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AddServerProviderTokenRouteArgs{server: $server, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [AppSettingsPage]
|
/// [AppSettingsPage]
|
||||||
class AppSettingsRoute extends PageRouteInfo<void> {
|
class AppSettingsRoute extends PageRouteInfo<void> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue