Skip to content

Commit

Permalink
feat: add save config feature
Browse files Browse the repository at this point in the history
  • Loading branch information
FriesI23 committed Jan 28, 2025
1 parent d095e6e commit dc4e20f
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 31 deletions.
8 changes: 8 additions & 0 deletions ios/Runner/Runner.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array/>
</dict>
</plist>
46 changes: 43 additions & 3 deletions lib/model/app_sync_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ enum AppSyncServerType implements EnumWithDBCode<AppSyncServerType> {
includeConnTimeoutField: true,
includeConnRetryCountField: true,
includeSyncNetworkField: true),
fake(code: 99);
fake(
code: 99,
includePasswordField: true,
);

final int code;
final bool includePathField;
Expand Down Expand Up @@ -102,6 +105,18 @@ abstract interface class AppSyncServer implements JsonAdaptor {
}
}

static AppSyncServer? fromForm(AppSyncServerForm? form) {
if (form == null) return null;
switch (form.type) {
case AppSyncServerType.webdav:
return AppWebDavSyncServer.fromForm(form);
case AppSyncServerType.fake:
return AppFakeSyncServer.fromForm(form);
default:
return null;
}
}

static AppSyncServer? newServer(AppSyncServerType type) {
final identity = const Uuid().v4();
switch (type) {
Expand All @@ -123,6 +138,9 @@ abstract interface class AppSyncServer implements JsonAdaptor {
bool get verified;
bool get configed;

String? get password;

bool isSameConfig(AppSyncServer other, {bool withoutPassword = false});
AppSyncServerForm toForm();
String toDebugString();
}
Expand Down Expand Up @@ -255,6 +273,16 @@ final class AppWebDavSyncServer implements AppSyncServer {
@JsonKey()
String get name => path.toString();

@override
bool isSameConfig(AppSyncServer other, {bool withoutPassword = false}) {
if (identical(this, other)) return true;
if (other is! AppWebDavSyncServer) return false;
return (identity == other.identity &&
path == other.path &&
username == other.username &&
(withoutPassword ? true : password == other.password));
}

@override
Map<String, dynamic> toJson() => _$AppWebDavSyncServerToJson(this);

Expand Down Expand Up @@ -320,6 +348,8 @@ final class AppFakeSyncServer implements AppSyncServer {
final bool verified;
@override
final bool configed;
@override
final String? password;

const AppFakeSyncServer({
required this.identity,
Expand All @@ -329,12 +359,12 @@ final class AppFakeSyncServer implements AppSyncServer {
required this.timeout,
required this.verified,
required this.configed,
required this.password,
}) : type = AppSyncServerType.fake;

factory AppFakeSyncServer.newServer({
required String identity,
required String path,
String username = '',
String password = '',
Duration? timeout,
}) {
Expand All @@ -347,6 +377,7 @@ final class AppFakeSyncServer implements AppSyncServer {
timeout: timeout,
verified: false,
configed: false,
password: password,
);
}

Expand All @@ -355,6 +386,7 @@ final class AppFakeSyncServer implements AppSyncServer {
required this.name,
required this.createTime,
required this.modifyTime,
this.password,
this.timeout,
required this.verified,
required this.configed,
Expand All @@ -371,7 +403,8 @@ final class AppFakeSyncServer implements AppSyncServer {
modifyTime: form.modifyTime,
timeout: form.timeout,
verified: form.verified,
configed: form.configed);
configed: form.configed,
password: form.password);

@override
String toDebugString() => """AppFakeSyncServer(
Expand All @@ -385,6 +418,13 @@ final class AppFakeSyncServer implements AppSyncServer {
configed=$configed,
)""";

@override
bool isSameConfig(AppSyncServer other, {bool withoutPassword = true}) {
if (identical(this, other)) return true;
if (other is! AppFakeSyncServer) return false;
return identity == other.identity;
}

@override
AppSyncServerForm toForm() => AppSyncServerForm(
uuid: UuidValue.fromString(identity),
Expand Down
20 changes: 20 additions & 0 deletions lib/model/app_sync_server.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 64 additions & 1 deletion lib/provider/app_sync.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

import '../model/app_sync_server.dart';
import '../persistent/profile/handler/app_sync.dart';
Expand All @@ -39,4 +40,66 @@ class AppSyncViewModel with ChangeNotifier, ProfileHandlerLoadedMixin {
}

AppSyncServer? get serverConfig => _serverConfig?.get();

Future<String?> getPassword({String? identity}) {
identity = identity ?? serverConfig?.identity;
if (identity == null) return Future.value(null);
return const FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
).read(key: "sync-pwd-$identity").catchError((e, s) {
if (kDebugMode) Error.throwWithStackTrace(e, s);
return serverConfig?.password;
});
}

Future<bool> setPassword({String? identity, required String? value}) async {
identity = identity ?? serverConfig?.identity;
if (identity == null) return false;
try {
const FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
mOptions: MacOsOptions(),
).write(key: "sync-pwd-$identity", value: value);
} catch (e, s) {
if (kDebugMode) Error.throwWithStackTrace(e, s);
return false;
}
return true;
}

Future<bool> saveWithConfigForm(AppSyncServerForm? form,
{bool forceSave = false,
bool resetStatus = true,
bool removable = false}) async {
AppSyncServer? oldConfig;
AppSyncServer? newConfig;
AppSyncServer? protoConfig;
bool? result;

oldConfig = serverConfig;
protoConfig = newConfig = AppSyncServer.fromForm(form);
if (newConfig == null && !removable) return false;
final isSameServer = switch ((oldConfig, newConfig)) {
(null, null) => true,
(null, _) || (_, null) => false,
(_, _) => oldConfig!.isSameConfig(newConfig!, withoutPassword: true),
};
if (isSameServer && !forceSave) return false;
if (!isSameServer || resetStatus) {
newConfig = AppSyncServer.fromForm(
form?.copyWith(configed: false, verified: false, password: ''));
}
result = await _serverConfig?.set(newConfig);
if (result != true) return false;
if (protoConfig != null) {
result = await setPassword(
identity: protoConfig.identity, value: protoConfig.password);
}
if (result != true) {
return await _serverConfig?.set(AppSyncServer.fromForm(
form?.copyWith(configed: false, verified: false))) ??
false;
}
return true;
}
}
24 changes: 14 additions & 10 deletions lib/provider/app_sync_server_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:uuid/uuid.dart';

import '../logging/helper.dart';
import '../model/app_sync_server.dart';
import 'app_sync.dart';
import 'commons.dart';

class AppSyncServerFormViewModel extends ChangeNotifier
Expand All @@ -31,7 +31,7 @@ class AppSyncServerFormViewModel extends ChangeNotifier

bool _mounted = true;
bool _pwdLoaded = false;
AppSyncServer? _crtServerConfig;
AppSyncViewModel? _parent;
Completer<(String, String?)>? _pwdCompleter;

late AppSyncServerForm _form;
Expand Down Expand Up @@ -81,15 +81,20 @@ class AppSyncServerFormViewModel extends ChangeNotifier
AppWebDavSyncServer.newServer(identity: const Uuid().v4(), path: '')
.toForm();

AppSyncServer? get crtServerConfig => initServerConfig ?? _crtServerConfig;
AppSyncServer? get crtServerConfig =>
initServerConfig ?? _parent?.serverConfig;

AppSyncServerForm get formSnapshot => _form.copyWith();

void updateCrtServerConfig(AppSyncServer? newConfig) {
if (newConfig != crtServerConfig) {
appLog.load.info("$runtimeType.updateCrtServerConfig", ex: [newConfig]);
_crtServerConfig = newConfig;
notifyListeners();
AppSyncServerForm getFinalForm() {
flushInputControllersToForm();
return _form.copyWith(modifyTime: DateTime.now());
}

void updateParentViewModel(AppSyncViewModel? parent) {
if (parent != _parent) {
appLog.load.info("$runtimeType.updateCrtServerConfig", ex: [parent]);
_parent = parent;
}
}

Expand Down Expand Up @@ -182,8 +187,7 @@ class AppSyncServerFormViewModel extends ChangeNotifier
if (crtCompleter != null) return crtCompleter.future;
final completer = _pwdCompleter = Completer<(String, String?)>();
final identity = this.identity;
const FlutterSecureStorage()
.read(key: identity)
(_parent?.getPassword(identity: identity) ?? Future.value(null))
.timeout(timeout)
.then((value) {
value = value ?? _form.password;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ class _AppSyncServerConnTimeoutTile
late AppSyncServerFormViewModel vm;
late AppSyncServerType crtType;

String get crtText => vm.connectTimeout?.inSeconds.toString() ?? '';

@override
void initState() {
vm = context.read<AppSyncServerFormViewModel>();
crtType = vm.type;
controller = TextEditingController.fromValue(
TextEditingValue(text: vm.connectTimeout?.inSeconds.toString() ?? ''));
controller =
TextEditingController.fromValue(TextEditingValue(text: crtText));
super.initState();
}

Expand All @@ -60,7 +62,7 @@ class _AppSyncServerConnTimeoutTile
super.didChangeDependencies();
if (crtType != vm.type) {
crtType = vm.type;
controller.clear();
controller.text = crtText;
}
}

Expand Down Expand Up @@ -125,12 +127,14 @@ class _AppSyncServerConnRetryCountTile
late AppSyncServerFormViewModel vm;
late AppSyncServerType crtType;

String get crtText => vm.connectRetryCount?.toString() ?? '';

@override
void initState() {
vm = context.read<AppSyncServerFormViewModel>();
crtType = vm.type;
controller = TextEditingController.fromValue(
TextEditingValue(text: vm.connectRetryCount?.toString() ?? ''));
controller =
TextEditingController.fromValue(TextEditingValue(text: crtText));
super.initState();
}

Expand All @@ -145,7 +149,7 @@ class _AppSyncServerConnRetryCountTile
super.didChangeDependencies();
if (crtType != vm.type) {
crtType = vm.type;
controller.clear();
controller.text = crtText;
}
}

Expand Down
Loading

0 comments on commit dc4e20f

Please sign in to comment.