mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-27 11:16:45 +00:00
Rewrite services cubit and add basic service screen.
This commit is contained in:
parent
62929a4839
commit
7d8f8e1d38
|
@ -37,7 +37,8 @@ class GenericJobMutationReturn extends GenericMutationResult {
|
||||||
final ServerJob? job;
|
final ServerJob? job;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerApi extends ApiMap with VolumeApi, JobsApi, ServerActionsApi {
|
class ServerApi extends ApiMap
|
||||||
|
with VolumeApi, JobsApi, ServerActionsApi, ServicesApi {
|
||||||
ServerApi({
|
ServerApi({
|
||||||
this.hasLogger = false,
|
this.hasLogger = false,
|
||||||
this.isWithToken = true,
|
this.isWithToken = true,
|
||||||
|
|
|
@ -409,11 +409,11 @@ class ServerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ServiceTypes.passwordManager: response.data['bitwarden'] == 0,
|
ServiceTypes.bitwarden: response.data['bitwarden'] == 0,
|
||||||
ServiceTypes.git: response.data['gitea'] == 0,
|
ServiceTypes.gitea: response.data['gitea'] == 0,
|
||||||
ServiceTypes.cloud: response.data['nextcloud'] == 0,
|
ServiceTypes.nextcloud: response.data['nextcloud'] == 0,
|
||||||
ServiceTypes.vpn: response.data['ocserv'] == 0,
|
ServiceTypes.ocserv: response.data['ocserv'] == 0,
|
||||||
ServiceTypes.socialNetwork: response.data['pleroma'] == 0,
|
ServiceTypes.pleroma: response.data['pleroma'] == 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,15 +907,15 @@ extension UrlServerExt on ServiceTypes {
|
||||||
// return ''; // external service
|
// return ''; // external service
|
||||||
// case ServiceTypes.video:
|
// case ServiceTypes.video:
|
||||||
// return ''; // jitsi meet not working
|
// return ''; // jitsi meet not working
|
||||||
case ServiceTypes.passwordManager:
|
case ServiceTypes.bitwarden:
|
||||||
return 'bitwarden';
|
return 'bitwarden';
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return 'nextcloud';
|
return 'nextcloud';
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return 'pleroma';
|
return 'pleroma';
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return 'gitea';
|
return 'gitea';
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return 'ocserv';
|
return 'ocserv';
|
||||||
default:
|
default:
|
||||||
throw Exception('wrong state');
|
throw Exception('wrong state');
|
||||||
|
|
|
@ -23,94 +23,86 @@ enum InitializingSteps {
|
||||||
enum Period { hour, day, month }
|
enum Period { hour, day, month }
|
||||||
|
|
||||||
enum ServiceTypes {
|
enum ServiceTypes {
|
||||||
mail,
|
mailserver,
|
||||||
messenger,
|
bitwarden,
|
||||||
passwordManager,
|
jitsi,
|
||||||
video,
|
nextcloud,
|
||||||
cloud,
|
pleroma,
|
||||||
socialNetwork,
|
gitea,
|
||||||
git,
|
ocserv,
|
||||||
vpn,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ServiceTypesExt on ServiceTypes {
|
extension ServiceTypesExt on ServiceTypes {
|
||||||
String get title {
|
String get title {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ServiceTypes.mail:
|
case ServiceTypes.mailserver:
|
||||||
return 'services.mail.title'.tr();
|
return 'services.mail.title'.tr();
|
||||||
case ServiceTypes.messenger:
|
case ServiceTypes.bitwarden:
|
||||||
return 'services.messenger.title'.tr();
|
|
||||||
case ServiceTypes.passwordManager:
|
|
||||||
return 'services.password_manager.title'.tr();
|
return 'services.password_manager.title'.tr();
|
||||||
case ServiceTypes.video:
|
case ServiceTypes.jitsi:
|
||||||
return 'services.video.title'.tr();
|
return 'services.video.title'.tr();
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return 'services.cloud.title'.tr();
|
return 'services.cloud.title'.tr();
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return 'services.social_network.title'.tr();
|
return 'services.social_network.title'.tr();
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return 'services.git.title'.tr();
|
return 'services.git.title'.tr();
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return 'services.vpn.title'.tr();
|
return 'services.vpn.title'.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String get subtitle {
|
String get subtitle {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ServiceTypes.mail:
|
case ServiceTypes.mailserver:
|
||||||
return 'services.mail.subtitle'.tr();
|
return 'services.mail.subtitle'.tr();
|
||||||
case ServiceTypes.messenger:
|
case ServiceTypes.bitwarden:
|
||||||
return 'services.messenger.subtitle'.tr();
|
|
||||||
case ServiceTypes.passwordManager:
|
|
||||||
return 'services.password_manager.subtitle'.tr();
|
return 'services.password_manager.subtitle'.tr();
|
||||||
case ServiceTypes.video:
|
case ServiceTypes.jitsi:
|
||||||
return 'services.video.subtitle'.tr();
|
return 'services.video.subtitle'.tr();
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return 'services.cloud.subtitle'.tr();
|
return 'services.cloud.subtitle'.tr();
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return 'services.social_network.subtitle'.tr();
|
return 'services.social_network.subtitle'.tr();
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return 'services.git.subtitle'.tr();
|
return 'services.git.subtitle'.tr();
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return 'services.vpn.subtitle'.tr();
|
return 'services.vpn.subtitle'.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String get loginInfo {
|
String get loginInfo {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ServiceTypes.mail:
|
case ServiceTypes.mailserver:
|
||||||
return 'services.mail.login_info'.tr();
|
return 'services.mail.login_info'.tr();
|
||||||
case ServiceTypes.messenger:
|
case ServiceTypes.bitwarden:
|
||||||
return 'services.messenger.login_info'.tr();
|
|
||||||
case ServiceTypes.passwordManager:
|
|
||||||
return 'services.password_manager.login_info'.tr();
|
return 'services.password_manager.login_info'.tr();
|
||||||
case ServiceTypes.video:
|
case ServiceTypes.jitsi:
|
||||||
return 'services.video.login_info'.tr();
|
return 'services.video.login_info'.tr();
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return 'services.cloud.login_info'.tr();
|
return 'services.cloud.login_info'.tr();
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return 'services.social_network.login_info'.tr();
|
return 'services.social_network.login_info'.tr();
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return 'services.git.login_info'.tr();
|
return 'services.git.login_info'.tr();
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String get subdomain {
|
String get subdomain {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ServiceTypes.passwordManager:
|
case ServiceTypes.bitwarden:
|
||||||
return 'password';
|
return 'password';
|
||||||
case ServiceTypes.video:
|
case ServiceTypes.jitsi:
|
||||||
return 'meet';
|
return 'meet';
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return 'cloud';
|
return 'cloud';
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return 'social';
|
return 'social';
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return 'git';
|
return 'git';
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
case ServiceTypes.messenger:
|
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -118,21 +110,19 @@ extension ServiceTypesExt on ServiceTypes {
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ServiceTypes.mail:
|
case ServiceTypes.mailserver:
|
||||||
return BrandIcons.envelope;
|
return BrandIcons.envelope;
|
||||||
case ServiceTypes.messenger:
|
case ServiceTypes.bitwarden:
|
||||||
return BrandIcons.messanger;
|
|
||||||
case ServiceTypes.passwordManager:
|
|
||||||
return BrandIcons.key;
|
return BrandIcons.key;
|
||||||
case ServiceTypes.video:
|
case ServiceTypes.jitsi:
|
||||||
return BrandIcons.webcam;
|
return BrandIcons.webcam;
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return BrandIcons.upload;
|
return BrandIcons.upload;
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return BrandIcons.social;
|
return BrandIcons.social;
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return BrandIcons.git;
|
return BrandIcons.git;
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return Icons.vpn_lock_outlined;
|
return Icons.vpn_lock_outlined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
}
|
}
|
||||||
if (job is ServiceToggleJob) {
|
if (job is ServiceToggleJob) {
|
||||||
hasServiceJobs = true;
|
hasServiceJobs = true;
|
||||||
await api.switchService(job.type, job.needToTurnOn);
|
await api.switchService(job.type.name, job.needToTurnOn);
|
||||||
}
|
}
|
||||||
if (job is CreateSSHKeyJob) {
|
if (job is CreateSSHKeyJob) {
|
||||||
await usersCubit.addSshKey(job.user, job.publicKey);
|
await usersCubit.addSshKey(job.user, job.publicKey);
|
||||||
|
|
|
@ -1,31 +1,48 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
|
||||||
part 'services_state.dart';
|
part 'services_state.dart';
|
||||||
|
|
||||||
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
|
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
|
||||||
ServicesCubit(final ServerInstallationCubit serverInstallationCubit)
|
ServicesCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit, ServicesState.allOff());
|
: super(serverInstallationCubit, const ServicesState.empty());
|
||||||
final ServerApi api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
Timer? timer;
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
final Map<ServiceTypes, bool> statuses = await api.servicesPowerCheck();
|
final List<Service> services = await api.getAllServices();
|
||||||
emit(
|
emit(
|
||||||
ServicesState(
|
ServicesState(
|
||||||
isPasswordManagerEnable: statuses[ServiceTypes.passwordManager]!,
|
services: services,
|
||||||
isCloudEnable: statuses[ServiceTypes.cloud]!,
|
|
||||||
isGitEnable: statuses[ServiceTypes.git]!,
|
|
||||||
isSocialNetworkEnable: statuses[ServiceTypes.socialNetwork]!,
|
|
||||||
isVpnEnable: statuses[ServiceTypes.vpn]!,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reload({final bool useTimer = false}) async {
|
||||||
|
final List<Service> services = await api.getAllServices();
|
||||||
|
emit(
|
||||||
|
ServicesState(
|
||||||
|
services: services,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (useTimer) {
|
||||||
|
timer = Timer(const Duration(seconds: 60), () => reload(useTimer: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clear() async {
|
void clear() async {
|
||||||
emit(ServicesState.allOff());
|
emit(const ServicesState.empty());
|
||||||
|
if (timer != null && timer!.isActive) {
|
||||||
|
timer!.cancel();
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,90 +1,43 @@
|
||||||
part of 'services_cubit.dart';
|
part of 'services_cubit.dart';
|
||||||
|
|
||||||
class ServicesState extends ServerInstallationDependendState {
|
class ServicesState extends ServerInstallationDependendState {
|
||||||
factory ServicesState.allOn() => const ServicesState(
|
|
||||||
isPasswordManagerEnable: true,
|
|
||||||
isCloudEnable: true,
|
|
||||||
isGitEnable: true,
|
|
||||||
isSocialNetworkEnable: true,
|
|
||||||
isVpnEnable: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
factory ServicesState.allOff() => const ServicesState(
|
|
||||||
isPasswordManagerEnable: false,
|
|
||||||
isCloudEnable: false,
|
|
||||||
isGitEnable: false,
|
|
||||||
isSocialNetworkEnable: false,
|
|
||||||
isVpnEnable: false,
|
|
||||||
);
|
|
||||||
const ServicesState({
|
const ServicesState({
|
||||||
required this.isPasswordManagerEnable,
|
required this.services,
|
||||||
required this.isCloudEnable,
|
|
||||||
required this.isGitEnable,
|
|
||||||
required this.isSocialNetworkEnable,
|
|
||||||
required this.isVpnEnable,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isPasswordManagerEnable;
|
const ServicesState.empty() : this(services: const []);
|
||||||
final bool isCloudEnable;
|
|
||||||
final bool isGitEnable;
|
|
||||||
final bool isSocialNetworkEnable;
|
|
||||||
final bool isVpnEnable;
|
|
||||||
|
|
||||||
ServicesState enableList(
|
final List<Service> services;
|
||||||
final List<ServiceTypes> list,
|
bool get isPasswordManagerEnable => services.firstWhere((final service) => service.id == 'bitwarden', orElse: () => Service.empty).isEnabled;
|
||||||
) =>
|
bool get isCloudEnable => services.firstWhere((final service) => service.id == 'nextcloud', orElse: () => Service.empty).isEnabled;
|
||||||
ServicesState(
|
bool get isGitEnable => services.firstWhere((final service) => service.id == 'gitea', orElse: () => Service.empty).isEnabled;
|
||||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
bool get isSocialNetworkEnable => services.firstWhere((final service) => service.id == 'pleroma', orElse: () => Service.empty).isEnabled;
|
||||||
? true
|
bool get isVpnEnable => services.firstWhere((final service) => service.id == 'ocserv', orElse: () => Service.empty).isEnabled;
|
||||||
: isPasswordManagerEnable,
|
|
||||||
isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable,
|
|
||||||
isGitEnable:
|
|
||||||
list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable,
|
|
||||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
|
||||||
? true
|
|
||||||
: isPasswordManagerEnable,
|
|
||||||
isVpnEnable:
|
|
||||||
list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable,
|
|
||||||
);
|
|
||||||
|
|
||||||
ServicesState disableList(
|
Service? getServiceById(final String id) {
|
||||||
final List<ServiceTypes> list,
|
final service = services.firstWhere((final service) => service.id == id, orElse: () => Service.empty);
|
||||||
) =>
|
if (service.id == 'empty') {
|
||||||
ServicesState(
|
return null;
|
||||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
}
|
||||||
? false
|
return service;
|
||||||
: isPasswordManagerEnable,
|
}
|
||||||
isCloudEnable:
|
|
||||||
list.contains(ServiceTypes.cloud) ? false : isCloudEnable,
|
|
||||||
isGitEnable:
|
|
||||||
list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable,
|
|
||||||
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
|
|
||||||
? false
|
|
||||||
: isPasswordManagerEnable,
|
|
||||||
isVpnEnable:
|
|
||||||
list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object> get props => [
|
||||||
isPasswordManagerEnable,
|
services,
|
||||||
isCloudEnable,
|
|
||||||
isGitEnable,
|
|
||||||
isSocialNetworkEnable,
|
|
||||||
isVpnEnable
|
|
||||||
];
|
];
|
||||||
|
|
||||||
bool isEnableByType(final ServiceTypes type) {
|
bool isEnableByType(final ServiceTypes type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ServiceTypes.passwordManager:
|
case ServiceTypes.bitwarden:
|
||||||
return isPasswordManagerEnable;
|
return isPasswordManagerEnable;
|
||||||
case ServiceTypes.cloud:
|
case ServiceTypes.nextcloud:
|
||||||
return isCloudEnable;
|
return isCloudEnable;
|
||||||
case ServiceTypes.socialNetwork:
|
case ServiceTypes.pleroma:
|
||||||
return isSocialNetworkEnable;
|
return isSocialNetworkEnable;
|
||||||
case ServiceTypes.git:
|
case ServiceTypes.gitea:
|
||||||
return isGitEnable;
|
return isGitEnable;
|
||||||
case ServiceTypes.vpn:
|
case ServiceTypes.ocserv:
|
||||||
return isVpnEnable;
|
return isVpnEnable;
|
||||||
default:
|
default:
|
||||||
throw Exception('wrong state');
|
throw Exception('wrong state');
|
||||||
|
|
|
@ -48,7 +48,7 @@ class ToggleJob extends ClientJob {
|
||||||
required final super.title,
|
required final super.title,
|
||||||
});
|
});
|
||||||
|
|
||||||
final dynamic type;
|
final ServiceTypes type;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [...super.props, type];
|
List<Object> get props => [...super.props, type];
|
||||||
|
@ -56,7 +56,7 @@ class ToggleJob extends ClientJob {
|
||||||
|
|
||||||
class ServiceToggleJob extends ToggleJob {
|
class ServiceToggleJob extends ToggleJob {
|
||||||
ServiceToggleJob({
|
ServiceToggleJob({
|
||||||
required final ServiceTypes super.type,
|
required final super.type,
|
||||||
required this.needToTurnOn,
|
required this.needToTurnOn,
|
||||||
}) : super(
|
}) : super(
|
||||||
title:
|
title:
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Service {
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.isEnabled,
|
required this.isEnabled,
|
||||||
required this.isRequired,
|
required this.isRequired,
|
||||||
|
required this.isMovable,
|
||||||
required this.status,
|
required this.status,
|
||||||
required this.storageUsage,
|
required this.storageUsage,
|
||||||
required this.svgIcon,
|
required this.svgIcon,
|
||||||
|
@ -26,6 +27,7 @@ class Service {
|
||||||
description: service.description,
|
description: service.description,
|
||||||
isEnabled: service.isEnabled,
|
isEnabled: service.isEnabled,
|
||||||
isRequired: service.isRequired,
|
isRequired: service.isRequired,
|
||||||
|
isMovable: service.isMovable,
|
||||||
status: ServiceStatus.fromGraphQL(service.status),
|
status: ServiceStatus.fromGraphQL(service.status),
|
||||||
storageUsage: ServiceStorageUsage(
|
storageUsage: ServiceStorageUsage(
|
||||||
used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)),
|
used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)),
|
||||||
|
@ -40,11 +42,29 @@ class Service {
|
||||||
url: service.url,
|
url: service.url,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static Service empty = Service(
|
||||||
|
id: 'empty',
|
||||||
|
displayName: '',
|
||||||
|
description: '',
|
||||||
|
isEnabled: false,
|
||||||
|
isRequired: false,
|
||||||
|
isMovable: false,
|
||||||
|
status: ServiceStatus.off,
|
||||||
|
storageUsage: ServiceStorageUsage(
|
||||||
|
used: DiskSize(byte: 0),
|
||||||
|
volume: '',
|
||||||
|
),
|
||||||
|
svgIcon: '',
|
||||||
|
dnsRecords: [],
|
||||||
|
url: '',
|
||||||
|
);
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final String displayName;
|
final String displayName;
|
||||||
final String description;
|
final String description;
|
||||||
final bool isEnabled;
|
final bool isEnabled;
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
|
final bool isMovable;
|
||||||
final ServiceStatus status;
|
final ServiceStatus status;
|
||||||
final ServiceStorageUsage storageUsage;
|
final ServiceStorageUsage storageUsage;
|
||||||
final String svgIcon;
|
final String svgIcon;
|
||||||
|
|
|
@ -25,9 +25,11 @@ class BrandCards {
|
||||||
static Widget filled({
|
static Widget filled({
|
||||||
required final Widget child,
|
required final Widget child,
|
||||||
final bool tertiary = false,
|
final bool tertiary = false,
|
||||||
|
final bool error = false,
|
||||||
}) =>
|
}) =>
|
||||||
_FilledCard(
|
_FilledCard(
|
||||||
tertiary: tertiary,
|
tertiary: tertiary,
|
||||||
|
error: error,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -81,10 +83,12 @@ class _FilledCard extends StatelessWidget {
|
||||||
const _FilledCard({
|
const _FilledCard({
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.tertiary,
|
required this.tertiary,
|
||||||
|
required this.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final bool tertiary;
|
final bool tertiary;
|
||||||
|
final bool error;
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => Card(
|
Widget build(final BuildContext context) => Card(
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
|
@ -92,7 +96,8 @@ class _FilledCard extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
color: tertiary
|
color: error ? Theme.of(context).colorScheme.errorContainer
|
||||||
|
: tertiary
|
||||||
? Theme.of(context).colorScheme.tertiaryContainer
|
? Theme.of(context).colorScheme.tertiaryContainer
|
||||||
: Theme.of(context).colorScheme.surfaceVariant,
|
: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
child: child,
|
child: child,
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/service.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_hero_screen/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class ServicePage extends StatefulWidget {
|
class ServicePage extends StatefulWidget {
|
||||||
const ServicePage({final super.key});
|
const ServicePage({required this.serviceId, final super.key});
|
||||||
|
|
||||||
|
final String serviceId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ServicePage> createState() => _ServicePageState();
|
State<ServicePage> createState() => _ServicePageState();
|
||||||
|
@ -11,73 +18,190 @@ class ServicePage extends StatefulWidget {
|
||||||
|
|
||||||
class _ServicePageState extends State<ServicePage> {
|
class _ServicePageState extends State<ServicePage> {
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => BrandHeroScreen(
|
Widget build(final BuildContext context) {
|
||||||
|
final Service? service =
|
||||||
|
context.watch<ServicesCubit>().state.getServiceById(widget.serviceId);
|
||||||
|
|
||||||
|
if (service == null) {
|
||||||
|
return const BrandHeroScreen(
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
Center(
|
||||||
Container(
|
child: CircularProgressIndicator(),
|
||||||
alignment: Alignment.center,
|
|
||||||
child: const Icon(
|
|
||||||
Icons.question_mark_outlined,
|
|
||||||
size: 48,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'My Incredible Service',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
BrandCards.outlined(
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 24),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.check_box_outlined,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 130),
|
|
||||||
child: const Text(''),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: null,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 24),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.language_outlined,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 130),
|
|
||||||
child: const Text('Your Cool Domain'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return BrandHeroScreen(
|
||||||
|
hasBackButton: true,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: SvgPicture.string(
|
||||||
|
service.svgIcon,
|
||||||
|
width: 48.0,
|
||||||
|
height: 48.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
service.displayName,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onBackground,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ServiceStatusCard(status: service.status),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (service.url != null)
|
||||||
|
ListTile(
|
||||||
|
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||||
|
onTap: () => _launchURL(service.url),
|
||||||
|
leading: const Icon(Icons.open_in_browser),
|
||||||
|
title: Text(
|
||||||
|
'Open in browser',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
service.url!.replaceAll('https://', ''),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ListTile(
|
||||||
|
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||||
|
onTap: () => {},
|
||||||
|
leading: const Icon(Icons.restart_alt_outlined),
|
||||||
|
title: Text(
|
||||||
|
'Restart service',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||||
|
onTap: () => {},
|
||||||
|
leading: const Icon(Icons.power_settings_new),
|
||||||
|
title: Text(
|
||||||
|
'Disable service',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (service.isMovable)
|
||||||
|
ListTile(
|
||||||
|
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||||
|
onTap: () => {},
|
||||||
|
leading: const Icon(Icons.drive_file_move_outlined),
|
||||||
|
title: Text(
|
||||||
|
'Move to another volume',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServiceStatusCard extends StatelessWidget {
|
||||||
|
const ServiceStatusCard({
|
||||||
|
required this.status,
|
||||||
|
final super.key,
|
||||||
|
});
|
||||||
|
final ServiceStatus status;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
switch (status) {
|
||||||
|
case ServiceStatus.active:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.check_circle_outline,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Up and running'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case ServiceStatus.inactive:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.stop_circle_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Stopped'),
|
||||||
|
),
|
||||||
|
tertiary: true,
|
||||||
|
);
|
||||||
|
case ServiceStatus.failed:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.error_outline,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Failed to start'),
|
||||||
|
),
|
||||||
|
error: true,
|
||||||
|
);
|
||||||
|
case ServiceStatus.off:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.power_settings_new,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Disabled'),
|
||||||
|
),
|
||||||
|
tertiary: true,
|
||||||
|
);
|
||||||
|
case ServiceStatus.activating:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.restart_alt_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Activating'),
|
||||||
|
),
|
||||||
|
tertiary: true,
|
||||||
|
);
|
||||||
|
case ServiceStatus.deactivating:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.restart_alt_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Deactivating'),
|
||||||
|
),
|
||||||
|
tertiary: true,
|
||||||
|
);
|
||||||
|
case ServiceStatus.reloading:
|
||||||
|
return BrandCards.filled(
|
||||||
|
child: const ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
Icons.restart_alt_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
title: Text('Restarting'),
|
||||||
|
),
|
||||||
|
tertiary: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _launchURL(final url) async {
|
||||||
|
try {
|
||||||
|
final Uri uri = Uri.parse(url);
|
||||||
|
await launchUrl(
|
||||||
|
uri,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/config/text_themes.dart';
|
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.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/cubit/jobs/jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
import 'package:selfprivacy/logic/models/state_types.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_header/brand_header.dart';
|
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
|
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
|
||||||
|
@ -18,17 +15,17 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
|
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: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/pages/services/service_page.dart';
|
||||||
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
|
||||||
|
|
||||||
const switchableServices = [
|
const switchableServices = [
|
||||||
ServiceTypes.passwordManager,
|
ServiceTypes.bitwarden,
|
||||||
ServiceTypes.cloud,
|
ServiceTypes.nextcloud,
|
||||||
ServiceTypes.socialNetwork,
|
ServiceTypes.pleroma,
|
||||||
ServiceTypes.git,
|
ServiceTypes.gitea,
|
||||||
ServiceTypes.vpn,
|
ServiceTypes.ocserv,
|
||||||
];
|
];
|
||||||
|
|
||||||
class ServicesPage extends StatefulWidget {
|
class ServicesPage extends StatefulWidget {
|
||||||
|
@ -39,18 +36,14 @@ class ServicesPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _launchURL(final url) async {
|
void _launchURL(final url) async {
|
||||||
final canLaunch = await canLaunchUrlString(url);
|
try {
|
||||||
|
final Uri uri = Uri.parse(url);
|
||||||
if (canLaunch) {
|
await launchUrl(
|
||||||
try {
|
uri,
|
||||||
await launchUrlString(
|
mode: LaunchMode.externalApplication,
|
||||||
url,
|
);
|
||||||
);
|
} catch (e) {
|
||||||
} catch (e) {
|
print(e);
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw 'Could not launch $url';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,23 +60,28 @@ class _ServicesPageState extends State<ServicesPage> {
|
||||||
title: 'basis.services'.tr(),
|
title: 'basis.services'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: RefreshIndicator(
|
||||||
padding: paddingH15V0,
|
onRefresh: () async {
|
||||||
children: [
|
context.read<ServicesCubit>().reload();
|
||||||
BrandText.body1('services.title'.tr()),
|
},
|
||||||
const SizedBox(height: 24),
|
child: ListView(
|
||||||
if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)],
|
padding: paddingH15V0,
|
||||||
...ServiceTypes.values
|
children: [
|
||||||
.map(
|
BrandText.body1('services.title'.tr()),
|
||||||
(final t) => Padding(
|
const SizedBox(height: 24),
|
||||||
padding: const EdgeInsets.only(
|
if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)],
|
||||||
bottom: 30,
|
...ServiceTypes.values
|
||||||
|
.map(
|
||||||
|
(final t) => Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 30,
|
||||||
|
),
|
||||||
|
child: _Card(serviceType: t),
|
||||||
),
|
),
|
||||||
child: _Card(serviceType: t),
|
)
|
||||||
),
|
.toList()
|
||||||
)
|
],
|
||||||
.toList()
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -97,7 +95,6 @@ class _Card extends StatelessWidget {
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final isReady = context.watch<ServerInstallationCubit>().state
|
final isReady = context.watch<ServerInstallationCubit>().state
|
||||||
is ServerInstallationFinished;
|
is ServerInstallationFinished;
|
||||||
final changeTab = context.read<ChangeTab>().onPress;
|
|
||||||
|
|
||||||
final serviceState = context.watch<ServicesCubit>().state;
|
final serviceState = context.watch<ServicesCubit>().state;
|
||||||
final jobsCubit = context.watch<JobsCubit>();
|
final jobsCubit = context.watch<JobsCubit>();
|
||||||
|
@ -118,21 +115,11 @@ class _Card extends StatelessWidget {
|
||||||
final domainName = UiHelpers.getDomainName(config);
|
final domainName = UiHelpers.getDomainName(config);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: isSwitchOn
|
onTap: () => Navigator.of(context).push(
|
||||||
? () => showDialog<void>(
|
materialRoute(
|
||||||
context: context,
|
ServicePage(serviceId: serviceType.name)
|
||||||
// isScrollControlled: true,
|
)
|
||||||
// backgroundColor: Colors.transparent,
|
),
|
||||||
builder: (final BuildContext context) => _ServiceDetails(
|
|
||||||
serviceType: serviceType,
|
|
||||||
status:
|
|
||||||
isSwitchOn ? StateType.stable : StateType.uninitialized,
|
|
||||||
title: serviceType.title,
|
|
||||||
icon: serviceType.icon,
|
|
||||||
changeTab: changeTab,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: BrandCards.big(
|
child: BrandCards.big(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -202,7 +189,7 @@ class _Card extends StatelessWidget {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (serviceType == ServiceTypes.mail)
|
if (serviceType == ServiceTypes.mailserver)
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -246,257 +233,3 @@ class _Card extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ServiceDetails extends StatelessWidget {
|
|
||||||
const _ServiceDetails({
|
|
||||||
required this.serviceType,
|
|
||||||
required this.icon,
|
|
||||||
required this.status,
|
|
||||||
required this.title,
|
|
||||||
required this.changeTab,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ServiceTypes serviceType;
|
|
||||||
final IconData icon;
|
|
||||||
final StateType status;
|
|
||||||
final String title;
|
|
||||||
final ValueChanged<int> changeTab;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(final BuildContext context) {
|
|
||||||
late Widget child;
|
|
||||||
|
|
||||||
final config = context.watch<ServerInstallationCubit>().state;
|
|
||||||
final domainName = UiHelpers.getDomainName(config);
|
|
||||||
|
|
||||||
final linksStyle = body1Style.copyWith(
|
|
||||||
fontSize: 15,
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.white
|
|
||||||
: BrandColors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
);
|
|
||||||
|
|
||||||
final textStyle = body1Style.copyWith(
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.white
|
|
||||||
: BrandColors.black,
|
|
||||||
);
|
|
||||||
switch (serviceType) {
|
|
||||||
case ServiceTypes.mail:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
child: Text(
|
|
||||||
'services.mail.bottom_sheet.2'.tr(),
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
changeTab(2);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.messenger:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text:
|
|
||||||
'services.messenger.bottom_sheet.1'.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.passwordManager:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.password_manager.bottom_sheet.1'
|
|
||||||
.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => _launchURL('https://password.$domainName'),
|
|
||||||
child: Text(
|
|
||||||
'password.$domainName',
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.video:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.video.bottom_sheet.1'.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => _launchURL('https://meet.$domainName'),
|
|
||||||
child: Text(
|
|
||||||
'meet.$domainName',
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.cloud:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => _launchURL('https://cloud.$domainName'),
|
|
||||||
child: Text(
|
|
||||||
'cloud.$domainName',
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.socialNetwork:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.social_network.bottom_sheet.1'
|
|
||||||
.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => _launchURL('https://social.$domainName'),
|
|
||||||
child: Text(
|
|
||||||
'social.$domainName',
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.git:
|
|
||||||
child = RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: 'services.git.bottom_sheet.1'.tr(args: [domainName]),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
const WidgetSpan(child: SizedBox(width: 5)),
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 0.8),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => _launchURL('https://git.$domainName'),
|
|
||||||
child: Text(
|
|
||||||
'git.$domainName',
|
|
||||||
style: linksStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ServiceTypes.vpn:
|
|
||||||
child = Text(
|
|
||||||
'services.vpn.bottom_sheet.1'.tr(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Dialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: SizedBox(
|
|
||||||
width: 350,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: paddingH15V30,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
IconStatusMask(
|
|
||||||
status: status,
|
|
||||||
child: Icon(icon, size: 40, color: Colors.white),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
BrandText.h2(title),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
child,
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
Center(
|
|
||||||
child: Container(
|
|
||||||
child: BrandButton.rised(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
text: 'basis.close'.tr(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue