Merge pull request 'refactor(service): Get rid of legacy common enums' (#129) from service-legacy into master

Reviewed-on: kherel/selfprivacy.org.app#129
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
pull/132/head^2
Inex Code 2022-10-06 09:43:12 +03:00
commit eb8a67b081
13 changed files with 89 additions and 218 deletions

View File

@ -13,6 +13,7 @@ import 'package:selfprivacy/logic/models/json/api_token.dart';
import 'package:selfprivacy/logic/models/json/backup.dart'; import 'package:selfprivacy/logic/models/json/backup.dart';
import 'package:selfprivacy/logic/models/json/device_token.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart';
class ApiResponse<D> { class ApiResponse<D> {
@ -380,13 +381,13 @@ class ServerApi extends ApiMap {
} }
Future<void> switchService( Future<void> switchService(
final ServiceTypes type, final Service service,
final bool needToTurnOn, final bool needToTurnOn,
) async { ) async {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
client.post( client.post(
'/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}', '/services/${service.id}/${needToTurnOn ? 'enable' : 'disable'}',
); );
} on DioError catch (e) { } on DioError catch (e) {
print(e.message); print(e.message);
@ -395,7 +396,7 @@ class ServerApi extends ApiMap {
} }
} }
Future<Map<ServiceTypes, bool>> servicesPowerCheck() async { Future<Map<String, bool>> servicesPowerCheck() async {
Response response; Response response;
final Dio client = await getClient(); final Dio client = await getClient();
@ -409,11 +410,11 @@ class ServerApi extends ApiMap {
} }
return { return {
ServiceTypes.bitwarden: response.data['bitwarden'] == 0, 'bitwarden': response.data['bitwarden'] == 0,
ServiceTypes.gitea: response.data['gitea'] == 0, 'gitea': response.data['gitea'] == 0,
ServiceTypes.nextcloud: response.data['nextcloud'] == 0, 'nextcloud': response.data['nextcloud'] == 0,
ServiceTypes.ocserv: response.data['ocserv'] == 0, 'ocserv': response.data['ocserv'] == 0,
ServiceTypes.pleroma: response.data['pleroma'] == 0, 'pleroma': response.data['pleroma'] == 0,
}; };
} }
@ -867,28 +868,3 @@ class ServerApi extends ApiMap {
return ApiResponse(statusCode: code, data: null); return ApiResponse(statusCode: code, data: null);
} }
} }
extension UrlServerExt on ServiceTypes {
String get url {
switch (this) {
// case ServiceTypes.mail:
// return ''; // cannot be switch off
// case ServiceTypes.messenger:
// return ''; // external service
// case ServiceTypes.video:
// return ''; // jitsi meet not working
case ServiceTypes.bitwarden:
return 'bitwarden';
case ServiceTypes.nextcloud:
return 'nextcloud';
case ServiceTypes.pleroma:
return 'pleroma';
case ServiceTypes.gitea:
return 'gitea';
case ServiceTypes.ocserv:
return 'ocserv';
default:
throw Exception('wrong state');
}
}
}

View File

@ -1,7 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
enum LoadingStatus { enum LoadingStatus {
uninitialized, uninitialized,
refreshing, refreshing,
@ -36,111 +32,3 @@ enum Period {
} }
} }
} }
enum ServiceTypes {
mailserver,
bitwarden,
jitsi,
nextcloud,
pleroma,
gitea,
ocserv,
}
extension ServiceTypesExt on ServiceTypes {
String get title {
switch (this) {
case ServiceTypes.mailserver:
return 'mail.title'.tr();
case ServiceTypes.bitwarden:
return 'password_manager.title'.tr();
case ServiceTypes.jitsi:
return 'video.title'.tr();
case ServiceTypes.nextcloud:
return 'cloud.title'.tr();
case ServiceTypes.pleroma:
return 'social_network.title'.tr();
case ServiceTypes.gitea:
return 'git.title'.tr();
case ServiceTypes.ocserv:
return 'vpn.title'.tr();
}
}
String get subtitle {
switch (this) {
case ServiceTypes.mailserver:
return 'mail.subtitle'.tr();
case ServiceTypes.bitwarden:
return 'password_manager.subtitle'.tr();
case ServiceTypes.jitsi:
return 'video.subtitle'.tr();
case ServiceTypes.nextcloud:
return 'cloud.subtitle'.tr();
case ServiceTypes.pleroma:
return 'social_network.subtitle'.tr();
case ServiceTypes.gitea:
return 'git.subtitle'.tr();
case ServiceTypes.ocserv:
return 'vpn.subtitle'.tr();
}
}
String get loginInfo {
switch (this) {
case ServiceTypes.mailserver:
return 'mail.login_info'.tr();
case ServiceTypes.bitwarden:
return 'password_manager.login_info'.tr();
case ServiceTypes.jitsi:
return 'video.login_info'.tr();
case ServiceTypes.nextcloud:
return 'cloud.login_info'.tr();
case ServiceTypes.pleroma:
return 'social_network.login_info'.tr();
case ServiceTypes.gitea:
return 'git.login_info'.tr();
case ServiceTypes.ocserv:
return '';
}
}
String get subdomain {
switch (this) {
case ServiceTypes.bitwarden:
return 'password';
case ServiceTypes.jitsi:
return 'meet';
case ServiceTypes.nextcloud:
return 'cloud';
case ServiceTypes.pleroma:
return 'social';
case ServiceTypes.gitea:
return 'git';
case ServiceTypes.ocserv:
default:
return '';
}
}
IconData get icon {
switch (this) {
case ServiceTypes.mailserver:
return BrandIcons.envelope;
case ServiceTypes.bitwarden:
return BrandIcons.key;
case ServiceTypes.jitsi:
return BrandIcons.webcam;
case ServiceTypes.nextcloud:
return BrandIcons.upload;
case ServiceTypes.pleroma:
return BrandIcons.social;
case ServiceTypes.gitea:
return BrandIcons.git;
case ServiceTypes.ocserv:
return Icons.vpn_lock_outlined;
}
}
String get txt => toString().split('.')[1];
}

View File

@ -45,10 +45,10 @@ class JobsCubit extends Cubit<JobsState> {
newJobsList.addAll((state as JobsStateWithJobs).clientJobList); newJobsList.addAll((state as JobsStateWithJobs).clientJobList);
} }
final bool needToRemoveJob = newJobsList final bool needToRemoveJob = newJobsList
.any((final el) => el is ServiceToggleJob && el.type == job.type); .any((final el) => el is ServiceToggleJob && el.id == job.id);
if (needToRemoveJob) { if (needToRemoveJob) {
final ClientJob removingJob = newJobsList.firstWhere( final ClientJob removingJob = newJobsList.firstWhere(
(final el) => el is ServiceToggleJob && el.type == job.type, (final el) => el is ServiceToggleJob && el.id == job.id,
); );
removeJob(removingJob.id); removeJob(removingJob.id);
} else { } else {
@ -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.name, job.needToTurnOn); await api.switchService(job.service.id, job.needToTurnOn);
} }
if (job is CreateSSHKeyJob) { if (job is CreateSSHKeyJob) {
await usersCubit.addSshKey(job.user, job.publicKey); await usersCubit.addSshKey(job.user, job.publicKey);

View File

@ -15,7 +15,6 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.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/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
@ -571,7 +570,7 @@ class ServerInstallationRepository {
); );
final String serverIp = await getServerIpFromDomain(serverDomain); final String serverIp = await getServerIpFromDomain(serverDomain);
if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) { if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) {
final Map<ServiceTypes, bool> apiResponse = final Map<String, bool> apiResponse =
await serverApi.servicesPowerCheck(); await serverApi.servicesPowerCheck();
if (apiResponse.isNotEmpty) { if (apiResponse.isNotEmpty) {
return ServerHostingDetails( return ServerHostingDetails(

View File

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; 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/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'; import 'package:selfprivacy/logic/models/service.dart';

View File

@ -63,17 +63,17 @@ class ServicesState extends ServerInstallationDependendState {
lockedServices, lockedServices,
]; ];
bool isEnableByType(final ServiceTypes type) { bool isEnableByType(final Service service) {
switch (type) { switch (service.id) {
case ServiceTypes.bitwarden: case 'bitwarden':
return isPasswordManagerEnable; return isPasswordManagerEnable;
case ServiceTypes.nextcloud: case 'nextcloud':
return isCloudEnable; return isCloudEnable;
case ServiceTypes.pleroma: case 'pleroma':
return isSocialNetworkEnable; return isSocialNetworkEnable;
case ServiceTypes.gitea: case 'gitea':
return isGitEnable; return isGitEnable;
case ServiceTypes.ocserv: case 'ocserv':
return isVpnEnable; return isVpnEnable;
default: default:
throw Exception('wrong state'); throw Exception('wrong state');

View File

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/utils/password_generator.dart'; import 'package:selfprivacy/utils/password_generator.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
@ -62,23 +63,23 @@ class DeleteUserJob extends ClientJob {
class ToggleJob extends ClientJob { class ToggleJob extends ClientJob {
ToggleJob({ ToggleJob({
required this.type, required final this.service,
required final super.title, required final super.title,
}); });
final ServiceTypes type; final Service service;
@override @override
List<Object> get props => [...super.props, type]; List<Object> get props => [...super.props, service];
} }
class ServiceToggleJob extends ToggleJob { class ServiceToggleJob extends ToggleJob {
ServiceToggleJob({ ServiceToggleJob({
required final super.type, required super.service,
required this.needToTurnOn, required this.needToTurnOn,
}) : super( }) : super(
title: title:
'${needToTurnOn ? "jobs.service_turn_on".tr() : "jobs.service_turn_off".tr()} ${type.title}', '${needToTurnOn ? "jobs.service_turn_on".tr() : "jobs.service_turn_off".tr()} ${service.displayName}',
); );
final bool needToTurnOn; final bool needToTurnOn;

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/disk_size.dart';
@ -20,6 +21,25 @@ class Service {
this.url, this.url,
}); });
/// TODO Turn loginInfo into dynamic data, not static!
String get loginInfo {
switch (id) {
case 'mailserver':
return 'mail.login_info'.tr();
case 'bitwarden':
return 'password_manager.login_info'.tr();
case 'jitsi':
return 'video.login_info'.tr();
case 'nextcloud':
return 'cloud.login_info'.tr();
case 'pleroma':
return 'social_network.login_info'.tr();
case 'gitea':
return 'git.login_info'.tr();
}
return '';
}
Service.fromGraphQL(final Query$AllServices$services$allServices service) Service.fromGraphQL(final Query$AllServices$services$allServices service)
: this( : this(
id: service.id, id: service.id,

View File

@ -4,11 +4,11 @@ import 'package:selfprivacy/logic/models/state_types.dart';
class IconStatusMask extends StatelessWidget { class IconStatusMask extends StatelessWidget {
const IconStatusMask({ const IconStatusMask({
required this.child, required this.icon,
required this.status, required this.status,
final super.key, final super.key,
}); });
final Icon child; final Widget icon;
final StateType status; final StateType status;
@ -42,7 +42,7 @@ class IconStatusMask extends StatelessWidget {
colors: colors, colors: colors,
tileMode: TileMode.mirror, tileMode: TileMode.mirror,
).createShader(bounds), ).createShader(bounds),
child: child, child: icon,
); );
} }
} }

View File

@ -148,12 +148,12 @@ class _Card extends StatelessWidget {
children: [ children: [
IconStatusMask( IconStatusMask(
status: state, status: state,
child: Icon(icon, size: 30, color: Colors.white), icon: Icon(icon, size: 30, color: Colors.white),
), ),
if (state != StateType.uninitialized) if (state != StateType.uninitialized)
IconStatusMask( IconStatusMask(
status: state, status: state,
child: Icon( icon: Icon(
state == StateType.stable state == StateType.stable
? Icons.check_circle_outline ? Icons.check_circle_outline
: state == StateType.warning : state == StateType.warning

View File

@ -80,7 +80,7 @@ class StorageCard extends StatelessWidget {
if (state != StateType.uninitialized) if (state != StateType.uninitialized)
IconStatusMask( IconStatusMask(
status: state, status: state,
child: Icon( icon: Icon(
diskStatus.isDiskOkay diskStatus.isDiskOkay
? Icons.check_circle_outline ? Icons.check_circle_outline
: Icons.error_outline, : Icons.error_outline,

View File

@ -91,7 +91,7 @@ class _ServicePageState extends State<ServicePage> {
onTap: () => { onTap: () => {
context.read<JobsCubit>().createOrRemoveServiceToggleJob( context.read<JobsCubit>().createOrRemoveServiceToggleJob(
ServiceToggleJob( ServiceToggleJob(
type: _idToLegacyType(service.id), service: service,
needToTurnOn: serviceDisabled, needToTurnOn: serviceDisabled,
), ),
), ),
@ -142,28 +142,6 @@ class _ServicePageState extends State<ServicePage> {
], ],
); );
} }
// TODO: Get rid as soon as possible
ServiceTypes _idToLegacyType(final String serviceId) {
switch (serviceId) {
case 'mailserver':
return ServiceTypes.mailserver;
case 'jitsi':
return ServiceTypes.jitsi;
case 'bitwarden':
return ServiceTypes.bitwarden;
case 'nextcloud':
return ServiceTypes.nextcloud;
case 'pleroma':
return ServiceTypes.pleroma;
case 'gitea':
return ServiceTypes.gitea;
case 'ocserv':
return ServiceTypes.ocserv;
default:
throw Exception('wrong state');
}
}
} }
class ServiceStatusCard extends StatelessWidget { class ServiceStatusCard extends StatelessWidget {

View File

@ -1,12 +1,14 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.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/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_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/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/logic/models/state_types.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';
@ -21,11 +23,11 @@ import 'package:selfprivacy/utils/ui_helpers.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
const switchableServices = [ const switchableServices = [
ServiceTypes.bitwarden, 'bitwarden',
ServiceTypes.nextcloud, 'nextcloud',
ServiceTypes.pleroma, 'pleroma',
ServiceTypes.gitea, 'gitea',
ServiceTypes.ocserv, 'ocserv',
]; ];
class ServicesPage extends StatefulWidget { class ServicesPage extends StatefulWidget {
@ -70,13 +72,16 @@ class _ServicesPageState extends State<ServicesPage> {
BrandText.body1('basis.services_title'.tr()), BrandText.body1('basis.services_title'.tr()),
const SizedBox(height: 24), const SizedBox(height: 24),
if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)], if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)],
...ServiceTypes.values ...context
.read<ServicesCubit>()
.state
.services
.map( .map(
(final t) => Padding( (final service) => Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
bottom: 30, bottom: 30,
), ),
child: _Card(serviceType: t), child: _Card(service: service),
), ),
) )
.toList() .toList()
@ -88,9 +93,9 @@ class _ServicesPageState extends State<ServicesPage> {
} }
class _Card extends StatelessWidget { class _Card extends StatelessWidget {
const _Card({required this.serviceType}); const _Card({required this.service});
final ServiceTypes serviceType; final Service service;
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
final isReady = context.watch<ServerInstallationCubit>().state final isReady = context.watch<ServerInstallationCubit>().state
@ -100,16 +105,16 @@ class _Card extends StatelessWidget {
final jobsCubit = context.watch<JobsCubit>(); final jobsCubit = context.watch<JobsCubit>();
final jobState = jobsCubit.state; final jobState = jobsCubit.state;
final switchableService = switchableServices.contains(serviceType); final switchableService = switchableServices.contains(service.id);
final hasSwitchJob = switchableService && final hasSwitchJob = switchableService &&
jobState is JobsStateWithJobs && jobState is JobsStateWithJobs &&
jobState.clientJobList.any( jobState.clientJobList.any(
(final el) => el is ServiceToggleJob && el.type == serviceType, (final el) => el is ServiceToggleJob && el.id == service.id,
); );
final isSwitchOn = isReady && final isSwitchOn = isReady &&
(!switchableServices.contains(serviceType) || (!switchableServices.contains(service.id) ||
serviceState.isEnableByType(serviceType)); serviceState.isEnableByType(service));
final config = context.watch<ServerInstallationCubit>().state; final config = context.watch<ServerInstallationCubit>().state;
final domainName = UiHelpers.getDomainName(config); final domainName = UiHelpers.getDomainName(config);
@ -117,7 +122,7 @@ class _Card extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: isReady onTap: isReady
? () => Navigator.of(context) ? () => Navigator.of(context)
.push(materialRoute(ServicePage(serviceId: serviceType.name))) .push(materialRoute(ServicePage(serviceId: service.id)))
: null, : null,
child: BrandCards.big( child: BrandCards.big(
child: Column( child: Column(
@ -128,7 +133,12 @@ class _Card extends StatelessWidget {
IconStatusMask( IconStatusMask(
status: status:
isSwitchOn ? StateType.stable : StateType.uninitialized, isSwitchOn ? StateType.stable : StateType.uninitialized,
child: Icon(serviceType.icon, size: 30, color: Colors.white), icon: SvgPicture.string(
service.svgIcon,
width: 30.0,
height: 30.0,
color: Theme.of(context).colorScheme.onBackground,
),
), ),
if (isReady && switchableService) ...[ if (isReady && switchableService) ...[
const Spacer(), const Spacer(),
@ -138,11 +148,11 @@ class _Card extends StatelessWidget {
if (hasSwitchJob) { if (hasSwitchJob) {
isActive = (jobState.clientJobList.firstWhere( isActive = (jobState.clientJobList.firstWhere(
(final el) => (final el) =>
el is ServiceToggleJob && el.type == serviceType, el is ServiceToggleJob && el.id == service.id,
) as ServiceToggleJob) ) as ServiceToggleJob)
.needToTurnOn; .needToTurnOn;
} else { } else {
isActive = serviceState.isEnableByType(serviceType); isActive = serviceState.isEnableByType(service);
} }
return BrandSwitch( return BrandSwitch(
@ -150,7 +160,7 @@ class _Card extends StatelessWidget {
onChanged: (final value) => onChanged: (final value) =>
jobsCubit.createOrRemoveServiceToggleJob( jobsCubit.createOrRemoveServiceToggleJob(
ServiceToggleJob( ServiceToggleJob(
type: serviceType, service: service,
needToTurnOn: value, needToTurnOn: value,
), ),
), ),
@ -167,17 +177,17 @@ class _Card extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
BrandText.h2(serviceType.title), BrandText.h2(service.displayName),
const SizedBox(height: 10), const SizedBox(height: 10),
if (serviceType.subdomain != '') if (service.url != '' && service.url != null)
Column( Column(
children: [ children: [
GestureDetector( GestureDetector(
onTap: () => _launchURL( onTap: () => _launchURL(
'https://${serviceType.subdomain}.$domainName', 'https://${service.url}',
), ),
child: Text( child: Text(
'${serviceType.subdomain}.$domainName', '${service.url}',
style: TextStyle( style: TextStyle(
color: color:
Theme.of(context).colorScheme.secondary, Theme.of(context).colorScheme.secondary,
@ -188,7 +198,7 @@ class _Card extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
], ],
), ),
if (serviceType == ServiceTypes.mailserver) if (service.id == 'mailserver')
Column( Column(
children: [ children: [
Text( Text(
@ -201,9 +211,9 @@ class _Card extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
], ],
), ),
BrandText.body2(serviceType.loginInfo), BrandText.body2(service.loginInfo),
const SizedBox(height: 10), const SizedBox(height: 10),
BrandText.body2(serviceType.subtitle), BrandText.body2(service.description),
const SizedBox(height: 10), const SizedBox(height: 10),
], ],
), ),