mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-10 19:03:12 +00:00
feat(services): Service settings
This commit is contained in:
parent
7dff880147
commit
ef51f27e91
|
@ -349,7 +349,13 @@
|
|||
"activating": "Activating",
|
||||
"deactivating": "Deactivating",
|
||||
"reloading": "Restarting"
|
||||
}
|
||||
},
|
||||
"settings": "Service settings",
|
||||
"modified": "Modified",
|
||||
"invalid_input": "Invalid input",
|
||||
"create_job": "Create job",
|
||||
"update_job": "Update job",
|
||||
"wait_for_jobs": "Server is busy with other jobs. Please wait until they are finished."
|
||||
},
|
||||
"mail": {
|
||||
"login_info": "Use username and password from users tab. IMAP port is 143 with STARTTLS, SMTP port is 587 with STARTTLS."
|
||||
|
@ -602,7 +608,8 @@
|
|||
"change_ssh_settings": "Change SSH settings",
|
||||
"update_dns_records": "Update DNS records",
|
||||
"dns_records_did_not_change": "No changes needed",
|
||||
"dns_records_changed": "DNS records updated"
|
||||
"dns_records_changed": "DNS records updated",
|
||||
"change_service_settings": "Change service settings for {}"
|
||||
},
|
||||
"validations": {
|
||||
"required": "Required",
|
||||
|
|
|
@ -130,6 +130,22 @@ enum BackupReason {
|
|||
PRE_RESTORE
|
||||
}
|
||||
|
||||
type BoolConfigItem implements ConfigItem {
|
||||
id: String!
|
||||
description: String!
|
||||
widget: String!
|
||||
type: String!
|
||||
value: Boolean!
|
||||
defaultValue: Boolean!
|
||||
}
|
||||
|
||||
interface ConfigItem {
|
||||
id: String!
|
||||
description: String!
|
||||
widget: String!
|
||||
type: String!
|
||||
}
|
||||
|
||||
"""Date with time (isoformat)"""
|
||||
scalar DateTime
|
||||
|
||||
|
@ -155,6 +171,16 @@ type DnsRecord {
|
|||
displayName: String!
|
||||
}
|
||||
|
||||
type EnumConfigItem implements ConfigItem {
|
||||
id: String!
|
||||
description: String!
|
||||
widget: String!
|
||||
type: String!
|
||||
value: String!
|
||||
defaultValue: String!
|
||||
options: [String!]!
|
||||
}
|
||||
|
||||
type GenericBackupConfigReturn implements MutationReturnInterface {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
|
@ -183,6 +209,11 @@ input InitializeRepositoryInput {
|
|||
password: String!
|
||||
}
|
||||
|
||||
"""
|
||||
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
|
||||
"""
|
||||
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
|
||||
|
||||
type Job {
|
||||
getJobs: [ApiJob!]!
|
||||
getJob(jobId: String!): ApiJob
|
||||
|
@ -192,6 +223,24 @@ type JobMutations {
|
|||
removeJob(jobId: String!): GenericMutationReturn!
|
||||
}
|
||||
|
||||
type LogEntry {
|
||||
message: String!
|
||||
timestamp: DateTime!
|
||||
priority: Int
|
||||
systemdUnit: String
|
||||
systemdSlice: String
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type Logs {
|
||||
paginated(limit: Int! = 20, upCursor: String = null, downCursor: String = null): PaginatedEntries!
|
||||
}
|
||||
|
||||
type LogsPageMeta {
|
||||
upCursor: String
|
||||
downCursor: String
|
||||
}
|
||||
|
||||
input MigrateToBindsInput {
|
||||
emailBlockDevice: String!
|
||||
bitwardenBlockDevice: String!
|
||||
|
@ -252,9 +301,18 @@ interface MutationReturnInterface {
|
|||
code: Int!
|
||||
}
|
||||
|
||||
type PaginatedEntries {
|
||||
"""Metadata to aid in pagination."""
|
||||
pageMeta: LogsPageMeta!
|
||||
|
||||
"""The list of log entries."""
|
||||
entries: [LogEntry!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
api: Api!
|
||||
system: System!
|
||||
logs: Logs!
|
||||
users: Users!
|
||||
storage: Storage!
|
||||
jobs: Job!
|
||||
|
@ -305,6 +363,7 @@ type Service {
|
|||
url: String
|
||||
dnsRecords: [DnsRecord!]
|
||||
storageUsage: ServiceStorageUsage!
|
||||
configuration: [ConfigItem!]
|
||||
backupSnapshots: [SnapshotInfo!]
|
||||
}
|
||||
|
||||
|
@ -350,9 +409,15 @@ type ServicesMutations {
|
|||
stopService(serviceId: String!): ServiceMutationReturn!
|
||||
startService(serviceId: String!): ServiceMutationReturn!
|
||||
restartService(serviceId: String!): ServiceMutationReturn!
|
||||
setServiceConfiguration(input: SetServiceConfigurationInput!): ServiceMutationReturn!
|
||||
moveService(input: MoveServiceInput!): ServiceJobMutationReturn!
|
||||
}
|
||||
|
||||
input SetServiceConfigurationInput {
|
||||
serviceId: String!
|
||||
configuration: JSON!
|
||||
}
|
||||
|
||||
enum Severity {
|
||||
INFO
|
||||
WARNING
|
||||
|
@ -408,9 +473,20 @@ type StorageVolume {
|
|||
usages: [StorageUsageInterface!]!
|
||||
}
|
||||
|
||||
type StringConfigItem implements ConfigItem {
|
||||
id: String!
|
||||
description: String!
|
||||
widget: String!
|
||||
type: String!
|
||||
value: String!
|
||||
defaultValue: String!
|
||||
regex: String
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
jobUpdates: [ApiJob!]!
|
||||
count: Int!
|
||||
logEntries: LogEntry!
|
||||
}
|
||||
|
||||
type System {
|
||||
|
|
|
@ -1111,6 +1111,138 @@ class _CopyWithStubImpl$Input$SSHSettingsInput<TRes>
|
|||
_res;
|
||||
}
|
||||
|
||||
class Input$SetServiceConfigurationInput {
|
||||
factory Input$SetServiceConfigurationInput({
|
||||
required String serviceId,
|
||||
required Map<String, dynamic> configuration,
|
||||
}) =>
|
||||
Input$SetServiceConfigurationInput._({
|
||||
r'serviceId': serviceId,
|
||||
r'configuration': configuration,
|
||||
});
|
||||
|
||||
Input$SetServiceConfigurationInput._(this._$data);
|
||||
|
||||
factory Input$SetServiceConfigurationInput.fromJson(
|
||||
Map<String, dynamic> data) {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$serviceId = data['serviceId'];
|
||||
result$data['serviceId'] = (l$serviceId as String);
|
||||
final l$configuration = data['configuration'];
|
||||
result$data['configuration'] = (l$configuration as Map<String, dynamic>);
|
||||
return Input$SetServiceConfigurationInput._(result$data);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$data;
|
||||
|
||||
String get serviceId => (_$data['serviceId'] as String);
|
||||
|
||||
Map<String, dynamic> get configuration =>
|
||||
(_$data['configuration'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$serviceId = serviceId;
|
||||
result$data['serviceId'] = l$serviceId;
|
||||
final l$configuration = configuration;
|
||||
result$data['configuration'] = l$configuration;
|
||||
return result$data;
|
||||
}
|
||||
|
||||
CopyWith$Input$SetServiceConfigurationInput<
|
||||
Input$SetServiceConfigurationInput>
|
||||
get copyWith => CopyWith$Input$SetServiceConfigurationInput(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Input$SetServiceConfigurationInput) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$serviceId = serviceId;
|
||||
final lOther$serviceId = other.serviceId;
|
||||
if (l$serviceId != lOther$serviceId) {
|
||||
return false;
|
||||
}
|
||||
final l$configuration = configuration;
|
||||
final lOther$configuration = other.configuration;
|
||||
if (l$configuration != lOther$configuration) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$serviceId = serviceId;
|
||||
final l$configuration = configuration;
|
||||
return Object.hashAll([
|
||||
l$serviceId,
|
||||
l$configuration,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CopyWith$Input$SetServiceConfigurationInput<TRes> {
|
||||
factory CopyWith$Input$SetServiceConfigurationInput(
|
||||
Input$SetServiceConfigurationInput instance,
|
||||
TRes Function(Input$SetServiceConfigurationInput) then,
|
||||
) = _CopyWithImpl$Input$SetServiceConfigurationInput;
|
||||
|
||||
factory CopyWith$Input$SetServiceConfigurationInput.stub(TRes res) =
|
||||
_CopyWithStubImpl$Input$SetServiceConfigurationInput;
|
||||
|
||||
TRes call({
|
||||
String? serviceId,
|
||||
Map<String, dynamic>? configuration,
|
||||
});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Input$SetServiceConfigurationInput<TRes>
|
||||
implements CopyWith$Input$SetServiceConfigurationInput<TRes> {
|
||||
_CopyWithImpl$Input$SetServiceConfigurationInput(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Input$SetServiceConfigurationInput _instance;
|
||||
|
||||
final TRes Function(Input$SetServiceConfigurationInput) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? serviceId = _undefined,
|
||||
Object? configuration = _undefined,
|
||||
}) =>
|
||||
_then(Input$SetServiceConfigurationInput._({
|
||||
..._instance._$data,
|
||||
if (serviceId != _undefined && serviceId != null)
|
||||
'serviceId': (serviceId as String),
|
||||
if (configuration != _undefined && configuration != null)
|
||||
'configuration': (configuration as Map<String, dynamic>),
|
||||
}));
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Input$SetServiceConfigurationInput<TRes>
|
||||
implements CopyWith$Input$SetServiceConfigurationInput<TRes> {
|
||||
_CopyWithStubImpl$Input$SetServiceConfigurationInput(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
String? serviceId,
|
||||
Map<String, dynamic>? configuration,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
||||
class Input$SshMutationInput {
|
||||
factory Input$SshMutationInput({
|
||||
required String username,
|
||||
|
@ -2152,5 +2284,10 @@ const possibleTypesMap = <String, Set<String>>{
|
|||
'TimezoneMutationReturn',
|
||||
'UserMutationReturn',
|
||||
},
|
||||
'ConfigItem': {
|
||||
'BoolConfigItem',
|
||||
'EnumConfigItem',
|
||||
'StringConfigItem',
|
||||
},
|
||||
'StorageUsageInterface': {'ServiceStorageUsage'},
|
||||
};
|
||||
|
|
|
@ -1,3 +1,32 @@
|
|||
fragment BoolConfigItem on BoolConfigItem {
|
||||
id
|
||||
description
|
||||
type
|
||||
boolValue: value
|
||||
defaultBoolValue: defaultValue
|
||||
widget
|
||||
}
|
||||
|
||||
fragment EnumConfigItem on EnumConfigItem {
|
||||
id
|
||||
description
|
||||
type
|
||||
stringValue: value
|
||||
defaultStringValue: defaultValue
|
||||
options
|
||||
widget
|
||||
}
|
||||
|
||||
fragment StringConfigItem on StringConfigItem {
|
||||
id
|
||||
description
|
||||
type
|
||||
stringValue: value
|
||||
defaultStringValue: defaultValue
|
||||
regex
|
||||
widget
|
||||
}
|
||||
|
||||
query AllServices {
|
||||
services {
|
||||
allServices {
|
||||
|
@ -22,6 +51,14 @@ query AllServices {
|
|||
}
|
||||
svgIcon
|
||||
url
|
||||
configuration {
|
||||
id
|
||||
description
|
||||
type
|
||||
... BoolConfigItem
|
||||
... EnumConfigItem
|
||||
... StringConfigItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,3 +114,10 @@ mutation MoveService($input: MoveServiceInput!) {
|
|||
}
|
||||
}
|
||||
|
||||
mutation SetServiceConfiguration($input: SetServiceConfigurationInput!) {
|
||||
services {
|
||||
setServiceConfiguration(input: $input) {
|
||||
...basicMutationReturnFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -174,4 +174,38 @@ mixin ServicesApi on GraphQLApiMap {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<GenericResult> setServiceConfiguration(
|
||||
final String serviceId,
|
||||
final Map<String, dynamic> settings,
|
||||
) async {
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables = Variables$Mutation$SetServiceConfiguration(
|
||||
input: Input$SetServiceConfigurationInput(
|
||||
serviceId: serviceId,
|
||||
configuration: settings,
|
||||
),
|
||||
);
|
||||
final mutation =
|
||||
Options$Mutation$SetServiceConfiguration(variables: variables);
|
||||
final response = await client.mutate$SetServiceConfiguration(mutation);
|
||||
return GenericResult(
|
||||
data: null,
|
||||
success:
|
||||
response.parsedData?.services.setServiceConfiguration.success ??
|
||||
false,
|
||||
code: response.parsedData?.services.setServiceConfiguration.code ?? 0,
|
||||
message: response.parsedData?.services.setServiceConfiguration.message,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return GenericResult(
|
||||
data: null,
|
||||
success: false,
|
||||
code: 0,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,11 @@ class JobsStateWithJobs extends JobsState {
|
|||
JobsState addJob(final ClientJob job) {
|
||||
if (job is ReplaceableJob) {
|
||||
final List<ClientJob> newJobsList = clientJobList
|
||||
.where((final element) => element.runtimeType != job.runtimeType)
|
||||
.where(
|
||||
(final element) => job.shouldReplaceOnlyIfSameId
|
||||
? element.runtimeType != job.runtimeType || element.id != job.id
|
||||
: element.runtimeType != job.runtimeType,
|
||||
)
|
||||
.toList();
|
||||
if (job.shouldRemoveInsteadOfAdd(clientJobList)) {
|
||||
getIt<NavigationService>().showSnackBar('jobs.job_removed'.tr());
|
||||
|
|
|
@ -242,6 +242,19 @@ class ApiConnectionRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<(bool, String)> setServiceConfiguration(
|
||||
final String serviceId,
|
||||
final Map<String, dynamic> settings,
|
||||
) async {
|
||||
final GenericResult result =
|
||||
await api.setServiceConfiguration(serviceId, settings);
|
||||
if (result.success) {
|
||||
return (true, result.message ?? 'basis.done'.tr());
|
||||
} else {
|
||||
return (false, result.message ?? 'jobs.generic_error'.tr());
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataStream.close();
|
||||
_connectionStatusStream.close();
|
||||
|
|
|
@ -367,9 +367,12 @@ abstract class ReplaceableJob extends ClientJob {
|
|||
super.id,
|
||||
super.status,
|
||||
super.message,
|
||||
super.requiresRebuild,
|
||||
super.requiresDnsUpdate,
|
||||
});
|
||||
|
||||
bool shouldRemoveInsteadOfAdd(final List<ClientJob> jobs) => false;
|
||||
bool get shouldReplaceOnlyIfSameId => false;
|
||||
}
|
||||
|
||||
class ChangeAutoUpgradeSettingsJob extends ReplaceableJob {
|
||||
|
@ -502,3 +505,45 @@ class ChangeSshSettingsJob extends ReplaceableJob {
|
|||
id: id,
|
||||
);
|
||||
}
|
||||
|
||||
class ChangeServiceConfiguration extends ReplaceableJob {
|
||||
ChangeServiceConfiguration({
|
||||
required this.serviceId,
|
||||
required this.serviceDisplayName,
|
||||
required this.settings,
|
||||
super.status,
|
||||
super.message,
|
||||
}) : super(
|
||||
title: 'jobs.change_service_settings'.tr(args: [serviceDisplayName]),
|
||||
id: 'change_settings_$serviceId',
|
||||
requiresDnsUpdate: true,
|
||||
requiresRebuild: true,
|
||||
);
|
||||
|
||||
final String serviceId;
|
||||
final String serviceDisplayName;
|
||||
final Map<String, dynamic> settings;
|
||||
|
||||
@override
|
||||
bool get shouldReplaceOnlyIfSameId => true;
|
||||
|
||||
@override
|
||||
Future<(bool, String)> execute() async => getIt<ApiConnectionRepository>()
|
||||
.setServiceConfiguration(serviceId, settings);
|
||||
|
||||
@override
|
||||
List<Object> get props => [...super.props, serviceId, settings];
|
||||
|
||||
@override
|
||||
ChangeServiceConfiguration copyWithNewStatus({
|
||||
required final JobStatusEnum status,
|
||||
final String? message,
|
||||
}) =>
|
||||
ChangeServiceConfiguration(
|
||||
serviceId: serviceId,
|
||||
serviceDisplayName: serviceDisplayName,
|
||||
settings: settings,
|
||||
status: status,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,16 @@ class Service extends Equatable {
|
|||
.toList() ??
|
||||
[],
|
||||
url: service.url,
|
||||
configuration: service.configuration
|
||||
?.map(
|
||||
(
|
||||
final Query$AllServices$services$allServices$configuration
|
||||
configItem,
|
||||
) =>
|
||||
ServiceConfigItem.fromGraphQL(configItem),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
const Service({
|
||||
required this.id,
|
||||
|
@ -50,6 +60,7 @@ class Service extends Equatable {
|
|||
required this.storageUsage,
|
||||
required this.svgIcon,
|
||||
required this.dnsRecords,
|
||||
required this.configuration,
|
||||
this.url,
|
||||
});
|
||||
|
||||
|
@ -89,6 +100,7 @@ class Service extends Equatable {
|
|||
svgIcon: '',
|
||||
dnsRecords: [],
|
||||
url: '',
|
||||
configuration: [],
|
||||
);
|
||||
|
||||
final String id;
|
||||
|
@ -104,6 +116,7 @@ class Service extends Equatable {
|
|||
final String svgIcon;
|
||||
final String? url;
|
||||
final List<DnsRecord> dnsRecords;
|
||||
final List<ServiceConfigItem> configuration;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
|
@ -120,6 +133,7 @@ class Service extends Equatable {
|
|||
svgIcon,
|
||||
dnsRecords,
|
||||
url,
|
||||
configuration,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -166,3 +180,146 @@ enum ServiceStatus {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ServiceConfigItem extends Equatable {
|
||||
const ServiceConfigItem({
|
||||
required this.id,
|
||||
required this.description,
|
||||
required this.widget,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
factory ServiceConfigItem.fromGraphQL(
|
||||
final Query$AllServices$services$allServices$configuration configItem,
|
||||
) =>
|
||||
configItem.when<ServiceConfigItem>(
|
||||
boolConfigItem: (final boolConfigItem) => BoolServiceConfigItem(
|
||||
id: boolConfigItem.id,
|
||||
description: boolConfigItem.description,
|
||||
widget: boolConfigItem.widget,
|
||||
type: boolConfigItem.type,
|
||||
value: boolConfigItem.boolValue,
|
||||
defaultValue: boolConfigItem.defaultBoolValue,
|
||||
),
|
||||
enumConfigItem: (final enumConfigItem) => EnumServiceConfigItem(
|
||||
id: enumConfigItem.id,
|
||||
description: enumConfigItem.description,
|
||||
widget: enumConfigItem.widget,
|
||||
type: enumConfigItem.type,
|
||||
value: enumConfigItem.stringValue,
|
||||
defaultValue: enumConfigItem.defaultStringValue,
|
||||
options: enumConfigItem.options,
|
||||
),
|
||||
stringConfigItem: (final stringConfigItem) => StringServiceConfigItem(
|
||||
id: stringConfigItem.id,
|
||||
description: stringConfigItem.description,
|
||||
widget: stringConfigItem.widget,
|
||||
type: stringConfigItem.type,
|
||||
value: stringConfigItem.stringValue,
|
||||
defaultValue: stringConfigItem.defaultStringValue,
|
||||
regex: stringConfigItem.regex,
|
||||
),
|
||||
orElse: () => FallbackServiceConfigItem(
|
||||
id: configItem.id,
|
||||
description: configItem.description,
|
||||
type: configItem.type,
|
||||
),
|
||||
);
|
||||
|
||||
final String id;
|
||||
final String description;
|
||||
final String widget;
|
||||
final String type;
|
||||
}
|
||||
|
||||
class StringServiceConfigItem extends ServiceConfigItem {
|
||||
const StringServiceConfigItem({
|
||||
required super.id,
|
||||
required super.description,
|
||||
required super.widget,
|
||||
required super.type,
|
||||
required this.value,
|
||||
required this.defaultValue,
|
||||
this.regex,
|
||||
});
|
||||
|
||||
final String value;
|
||||
final String defaultValue;
|
||||
final String? regex;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[id, description, widget, type, value, defaultValue, regex];
|
||||
}
|
||||
|
||||
class BoolServiceConfigItem extends ServiceConfigItem {
|
||||
const BoolServiceConfigItem({
|
||||
required super.id,
|
||||
required super.description,
|
||||
required super.widget,
|
||||
required super.type,
|
||||
required this.value,
|
||||
required this.defaultValue,
|
||||
});
|
||||
|
||||
final bool value;
|
||||
final bool defaultValue;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[id, description, widget, type, value, defaultValue];
|
||||
}
|
||||
|
||||
class EnumServiceConfigItem extends ServiceConfigItem {
|
||||
const EnumServiceConfigItem({
|
||||
required super.id,
|
||||
required super.description,
|
||||
required super.widget,
|
||||
required super.type,
|
||||
required this.value,
|
||||
required this.defaultValue,
|
||||
required this.options,
|
||||
});
|
||||
|
||||
final String value;
|
||||
final String defaultValue;
|
||||
final List<String> options;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[id, description, widget, type, value, defaultValue, options];
|
||||
}
|
||||
|
||||
class FallbackServiceConfigItem extends ServiceConfigItem {
|
||||
const FallbackServiceConfigItem({
|
||||
required super.id,
|
||||
required super.description,
|
||||
required super.type,
|
||||
}) : super(widget: 'fallback');
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, description, widget, type];
|
||||
}
|
||||
|
||||
// TODO: Not used yet by the API
|
||||
class IntServiceConfigItem extends ServiceConfigItem {
|
||||
const IntServiceConfigItem({
|
||||
required super.id,
|
||||
required super.description,
|
||||
required super.widget,
|
||||
required super.type,
|
||||
required this.value,
|
||||
required this.defaultValue,
|
||||
required this.min,
|
||||
required this.max,
|
||||
});
|
||||
|
||||
final int value;
|
||||
final int defaultValue;
|
||||
final int min;
|
||||
final int max;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[id, description, widget, type, value, defaultValue, min, max];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
part of '../service_settings_page.dart';
|
||||
|
||||
class BasicBoolConfigItem extends StatefulWidget {
|
||||
const BasicBoolConfigItem({
|
||||
required this.configItem,
|
||||
required this.onChanged,
|
||||
this.newValue,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final BoolServiceConfigItem configItem;
|
||||
final Function(bool) onChanged;
|
||||
final bool? newValue;
|
||||
|
||||
@override
|
||||
State<BasicBoolConfigItem> createState() => _BasicBoolConfigItemState();
|
||||
}
|
||||
|
||||
class _BasicBoolConfigItemState extends State<BasicBoolConfigItem> {
|
||||
@override
|
||||
Widget build(final BuildContext context) => Column(
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
title: Text(widget.configItem.description),
|
||||
subtitle: (widget.newValue != null &&
|
||||
widget.newValue != widget.configItem.value)
|
||||
? Text('service_page.modified'.tr())
|
||||
: null,
|
||||
value: widget.newValue ?? widget.configItem.value,
|
||||
onChanged: (final bool value) {
|
||||
widget.onChanged(value);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
part of '../service_settings_page.dart';
|
||||
|
||||
class BasicEnumConfigItem extends StatefulWidget {
|
||||
const BasicEnumConfigItem({
|
||||
required this.configItem,
|
||||
required this.onChanged,
|
||||
this.newValue,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final EnumServiceConfigItem configItem;
|
||||
final Function(String) onChanged;
|
||||
final String? newValue;
|
||||
|
||||
@override
|
||||
State<BasicEnumConfigItem> createState() => _BasicEnumConfigItemState();
|
||||
}
|
||||
|
||||
class _BasicEnumConfigItemState extends State<BasicEnumConfigItem> {
|
||||
@override
|
||||
Widget build(final BuildContext context) => Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(widget.configItem.description),
|
||||
subtitle: (widget.newValue != null &&
|
||||
widget.newValue != widget.configItem.value)
|
||||
? Text('service_page.modified'.tr())
|
||||
: null,
|
||||
trailing: DropdownButton<String>(
|
||||
value: widget.newValue ?? widget.configItem.value,
|
||||
items: widget.configItem.options
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(final String option) => DropdownMenuItem<String>(
|
||||
value: option,
|
||||
child: Text(option),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (final String? value) {
|
||||
widget.onChanged(value!);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
part of '../service_settings_page.dart';
|
||||
|
||||
class BasicStringConfigItem extends StatefulWidget {
|
||||
const BasicStringConfigItem({
|
||||
required this.configItem,
|
||||
required this.onChanged,
|
||||
this.newValue,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final StringServiceConfigItem configItem;
|
||||
final Function(String, bool) onChanged;
|
||||
final String? newValue;
|
||||
|
||||
@override
|
||||
State<BasicStringConfigItem> createState() => _BasicStringConfigItemState();
|
||||
}
|
||||
|
||||
class _BasicStringConfigItemState extends State<BasicStringConfigItem> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
bool _isValid = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.text = widget.newValue ?? widget.configItem.value;
|
||||
_controller.addListener(() {
|
||||
final String value = _controller.text;
|
||||
final bool isValid = _validateInput(value);
|
||||
if (isValid) {
|
||||
widget.onChanged(value, isValid);
|
||||
} else {
|
||||
setState(() {
|
||||
widget.onChanged(widget.newValue ?? widget.configItem.value, isValid);
|
||||
_isValid = isValid;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _validateInput(final String value) {
|
||||
if (value == '') {
|
||||
return false;
|
||||
}
|
||||
final regexPattern = widget.configItem.regex;
|
||||
if (regexPattern == null) {
|
||||
return true;
|
||||
}
|
||||
final regex = RegExp(regexPattern);
|
||||
return regex.hasMatch(value);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: _controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: widget.configItem.description,
|
||||
hintText: widget.configItem.value,
|
||||
counter: _controller.text != widget.configItem.value
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
_controller.text = widget.configItem.value;
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'service_page.modified'.tr(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(8.0),
|
||||
Icon(
|
||||
Icons.undo_outlined,
|
||||
size: 16.0,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
' ',
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: _isValid ? null : 'service_page.invalid_input'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
part of '../service_settings_page.dart';
|
||||
|
||||
class DomainStringConfigItem extends StatefulWidget {
|
||||
const DomainStringConfigItem({
|
||||
required this.configItem,
|
||||
required this.onChanged,
|
||||
this.newValue,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final StringServiceConfigItem configItem;
|
||||
final Function(String, bool) onChanged;
|
||||
final String? newValue;
|
||||
|
||||
@override
|
||||
State<DomainStringConfigItem> createState() => _DomainStringConfigItemState();
|
||||
}
|
||||
|
||||
class _DomainStringConfigItemState extends State<DomainStringConfigItem> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
bool _isValid = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.text = widget.newValue ?? widget.configItem.value;
|
||||
_controller.addListener(() {
|
||||
final String value = _controller.text;
|
||||
final bool isValid = _validateInput(value);
|
||||
if (isValid) {
|
||||
setState(() {
|
||||
widget.onChanged(value, isValid);
|
||||
_isValid = isValid;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
widget.onChanged(widget.newValue ?? widget.configItem.value, isValid);
|
||||
_isValid = isValid;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _validateInput(final String value) {
|
||||
if (value == '') {
|
||||
return false;
|
||||
}
|
||||
final regexPattern = widget.configItem.regex;
|
||||
if (regexPattern == null) {
|
||||
return true;
|
||||
}
|
||||
final regex = RegExp(regexPattern);
|
||||
return regex.hasMatch(value);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final String domain =
|
||||
getIt<ResourcesModel>().serverDomain?.domainName ?? '';
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: _controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: widget.configItem.description,
|
||||
hintText: widget.configItem.value,
|
||||
suffixText: '.$domain',
|
||||
counter: _controller.text != widget.configItem.value
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
_controller.text = widget.configItem.value;
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'service_page.modified'.tr(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(8.0),
|
||||
Icon(
|
||||
Icons.undo_outlined,
|
||||
size: 16.0,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
' ',
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: _isValid ? null : 'service_page.invalid_input'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -96,23 +96,36 @@ class _ServicePageState extends State<ServicePage> {
|
|||
),
|
||||
enabled: !serviceDisabled && !serviceLocked,
|
||||
),
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
onTap: () => context.read<JobsCubit>().addJob(
|
||||
ServiceToggleJob(
|
||||
service: service,
|
||||
needToTurnOn: serviceDisabled,
|
||||
if (!service.isRequired)
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
onTap: () => context.read<JobsCubit>().addJob(
|
||||
ServiceToggleJob(
|
||||
service: service,
|
||||
needToTurnOn: serviceDisabled,
|
||||
),
|
||||
),
|
||||
),
|
||||
leading: const Icon(Icons.power_settings_new),
|
||||
title: Text(
|
||||
serviceDisabled
|
||||
? 'service_page.enable'.tr()
|
||||
: 'service_page.disable'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
leading: const Icon(Icons.power_settings_new),
|
||||
title: Text(
|
||||
serviceDisabled
|
||||
? 'service_page.enable'.tr()
|
||||
: 'service_page.disable'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
enabled: !serviceLocked,
|
||||
),
|
||||
if (service.configuration.isNotEmpty)
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
onTap: () => context.pushRoute(
|
||||
ServiceSettingsRoute(serviceId: service.id),
|
||||
),
|
||||
leading: const Icon(Icons.settings_outlined),
|
||||
title: Text(
|
||||
'service_page.settings'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
enabled: !serviceLocked,
|
||||
),
|
||||
if (service.isMovable)
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
|
|
238
lib/ui/pages/services/service_settings_page.dart
Normal file
238
lib/ui/pages/services/service_settings_page.dart
Normal file
|
@ -0,0 +1,238 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/get_it/resources_model.dart';
|
||||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
|
||||
part 'config_item_fields/basic_string_config_item.dart';
|
||||
part 'config_item_fields/basic_bool_config_item.dart';
|
||||
part 'config_item_fields/basic_enum_config_item.dart';
|
||||
part 'config_item_fields/domain_string_config_item.dart';
|
||||
|
||||
@RoutePage()
|
||||
class ServiceSettingsPage extends StatefulWidget {
|
||||
const ServiceSettingsPage({required this.serviceId, super.key});
|
||||
|
||||
final String serviceId;
|
||||
|
||||
@override
|
||||
State<ServiceSettingsPage> createState() => _ServiceSettingsPageState();
|
||||
}
|
||||
|
||||
class _ServiceSettingsPageState extends State<ServiceSettingsPage> {
|
||||
Map<String, dynamic> settings = {};
|
||||
bool isFormValid = true;
|
||||
bool isJobAlreadyExists = false;
|
||||
|
||||
Widget configurationItemToWidget(
|
||||
final BuildContext context,
|
||||
final ServiceConfigItem configItem,
|
||||
final Map<String, dynamic> settings,
|
||||
) {
|
||||
switch (configItem) {
|
||||
case StringServiceConfigItem():
|
||||
void onChanged(final String value, final bool isFieldValid) {
|
||||
if (isFieldValid) {
|
||||
setState(() {
|
||||
if (value == configItem.value) {
|
||||
settings.remove(configItem.id);
|
||||
} else {
|
||||
settings[configItem.id] = value;
|
||||
}
|
||||
isFormValid = true;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
isFormValid = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (configItem.widget == 'subdomain') {
|
||||
return DomainStringConfigItem(
|
||||
configItem: configItem,
|
||||
newValue: settings[configItem.id],
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
return BasicStringConfigItem(
|
||||
configItem: configItem,
|
||||
newValue: settings[configItem.id],
|
||||
onChanged: onChanged,
|
||||
);
|
||||
case BoolServiceConfigItem():
|
||||
void onChanged(final bool value) {
|
||||
setState(() {
|
||||
if (value == configItem.value) {
|
||||
settings.remove(configItem.id);
|
||||
} else {
|
||||
settings[configItem.id] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
return BasicBoolConfigItem(
|
||||
configItem: configItem,
|
||||
newValue: settings[configItem.id],
|
||||
onChanged: onChanged,
|
||||
);
|
||||
|
||||
case EnumServiceConfigItem():
|
||||
void onChanged(final String value) {
|
||||
setState(() {
|
||||
if (value == configItem.value) {
|
||||
settings.remove(configItem.id);
|
||||
} else {
|
||||
settings[configItem.id] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
return BasicEnumConfigItem(
|
||||
configItem: configItem,
|
||||
newValue: settings[configItem.id],
|
||||
onChanged: onChanged,
|
||||
);
|
||||
case FallbackServiceConfigItem():
|
||||
return ListTile(
|
||||
title: Text(configItem.description),
|
||||
subtitle: Text(configItem.id),
|
||||
trailing: Text(configItem.type),
|
||||
leading: const Icon(Icons.error),
|
||||
);
|
||||
case IntServiceConfigItem():
|
||||
return ListTile(
|
||||
title: Text(configItem.description),
|
||||
subtitle: Text(configItem.id),
|
||||
trailing: Text(configItem.value.toString()),
|
||||
leading: const Icon(Icons.error),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final JobsState state = context.read<JobsCubit>().state;
|
||||
|
||||
if (state is JobsStateWithJobs) {
|
||||
final ChangeServiceConfiguration? existingJob =
|
||||
state.clientJobList.firstWhereOrNull(
|
||||
(final ClientJob job) =>
|
||||
job is ChangeServiceConfiguration &&
|
||||
job.serviceId == widget.serviceId,
|
||||
) as ChangeServiceConfiguration?;
|
||||
if (existingJob != null) {
|
||||
setState(() {
|
||||
settings = existingJob.settings;
|
||||
isJobAlreadyExists = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final Service? service =
|
||||
context.watch<ServicesBloc>().state.getServiceById(widget.serviceId);
|
||||
|
||||
if (service == null) {
|
||||
return const BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
children: [
|
||||
Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final JobsState state = context.watch<JobsCubit>().state;
|
||||
|
||||
if (state is JobsStateLoading) {
|
||||
return BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
hasFlashButton: true,
|
||||
heroIconWidget: SvgPicture.string(
|
||||
service.svgIcon,
|
||||
width: 48.0,
|
||||
height: 48.0,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
heroTitle: service.displayName,
|
||||
heroSubtitle: 'service_page.settings'.tr(),
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'service_page.wait_for_jobs'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Gap(16.0),
|
||||
const CircularProgressIndicator.adaptive(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final bool isModified = settings.isNotEmpty;
|
||||
|
||||
return BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
hasFlashButton: true,
|
||||
heroIconWidget: SvgPicture.string(
|
||||
service.svgIcon,
|
||||
width: 48.0,
|
||||
height: 48.0,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
heroTitle: service.displayName,
|
||||
heroSubtitle: 'service_page.settings'.tr(),
|
||||
children: [
|
||||
...service.configuration.map(
|
||||
(final ServiceConfigItem configItem) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: configurationItemToWidget(context, configItem, settings),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: FilledButton(
|
||||
onPressed: (isModified && isFormValid)
|
||||
? () {
|
||||
context.read<JobsCubit>().addJob(
|
||||
ChangeServiceConfiguration(
|
||||
serviceId: service.id,
|
||||
serviceDisplayName: service.displayName,
|
||||
settings: settings,
|
||||
),
|
||||
);
|
||||
context.router.maybePop();
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
isJobAlreadyExists
|
||||
? 'service_page.update_job'.tr()
|
||||
: 'service_page.create_job'.tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_mig
|
|||
import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/service_page.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/service_settings_page.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/services.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
|
||||
|
@ -96,6 +97,7 @@ class RootRouter extends _$RootRouter {
|
|||
AutoRoute(page: AboutApplicationRoute.page),
|
||||
AutoRoute(page: DeveloperSettingsRoute.page),
|
||||
AutoRoute(page: ServiceRoute.page),
|
||||
AutoRoute(page: ServiceSettingsRoute.page),
|
||||
AutoRoute(page: ServerDetailsRoute.page),
|
||||
AutoRoute(page: DnsDetailsRoute.page),
|
||||
AutoRoute(page: BackupDetailsRoute.page),
|
||||
|
@ -120,6 +122,8 @@ String getRouteTitle(final String routeName) {
|
|||
case 'ServicesRoute':
|
||||
case 'ServiceRoute':
|
||||
return 'basis.services';
|
||||
case 'ServiceSettingsRoute':
|
||||
return 'service_page.settings';
|
||||
case 'UsersRoute':
|
||||
return 'basis.users';
|
||||
case 'MoreRoute':
|
||||
|
|
|
@ -158,6 +158,16 @@ abstract class _$RootRouter extends RootStackRouter {
|
|||
),
|
||||
);
|
||||
},
|
||||
ServiceSettingsRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServiceSettingsRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServiceSettingsPage(
|
||||
serviceId: args.serviceId,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ServicesMigrationRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServicesMigrationRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
|
@ -590,6 +600,44 @@ class ServiceRouteArgs {
|
|||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServiceSettingsPage]
|
||||
class ServiceSettingsRoute extends PageRouteInfo<ServiceSettingsRouteArgs> {
|
||||
ServiceSettingsRoute({
|
||||
required String serviceId,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServiceSettingsRoute.name,
|
||||
args: ServiceSettingsRouteArgs(
|
||||
serviceId: serviceId,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServiceSettingsRoute';
|
||||
|
||||
static const PageInfo<ServiceSettingsRouteArgs> page =
|
||||
PageInfo<ServiceSettingsRouteArgs>(name);
|
||||
}
|
||||
|
||||
class ServiceSettingsRouteArgs {
|
||||
const ServiceSettingsRouteArgs({
|
||||
required this.serviceId,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String serviceId;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServiceSettingsRouteArgs{serviceId: $serviceId, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicesMigrationPage]
|
||||
class ServicesMigrationRoute extends PageRouteInfo<ServicesMigrationRouteArgs> {
|
||||
|
|
Loading…
Reference in a new issue