From 24db155811182ca07e0402800ca1e88964745f1c Mon Sep 17 00:00:00 2001
From: Peter Jankuliak
Date: Fri, 20 Sep 2024 13:03:47 +0200
Subject: [PATCH 1/5] Make sync without WiFi off by default + settings
migrations
---
lib/app/models/repo_location.dart | 2 +
.../atomic_shared_prefs_settings_key.dart | 8 +
lib/app/utils/settings/settings.dart | 23 +-
lib/app/utils/settings/v1.dart | 64 +++-
lib/app/utils/settings/v2.dart | 277 ++++++++++++++++++
5 files changed, 360 insertions(+), 14 deletions(-)
create mode 100644 lib/app/utils/settings/atomic_shared_prefs_settings_key.dart
create mode 100644 lib/app/utils/settings/v2.dart
diff --git a/lib/app/models/repo_location.dart b/lib/app/models/repo_location.dart
index c7aa9d4b..d1839b71 100644
--- a/lib/app/models/repo_location.dart
+++ b/lib/app/models/repo_location.dart
@@ -37,6 +37,8 @@ class RepoLocation implements Comparable {
RepoLocation move(io.Directory newDir) =>
RepoLocation._(newDir.path, _name, _ext);
+ RepoLocation clone() => RepoLocation._(this._dir, this._name, this._ext);
+
@override
bool operator ==(Object other) =>
other is RepoLocation && p.equals(path, other.path);
diff --git a/lib/app/utils/settings/atomic_shared_prefs_settings_key.dart b/lib/app/utils/settings/atomic_shared_prefs_settings_key.dart
new file mode 100644
index 00000000..ed2cb908
--- /dev/null
+++ b/lib/app/utils/settings/atomic_shared_prefs_settings_key.dart
@@ -0,0 +1,8 @@
+// In Settings V0 we used the SharedPreferences API to set the keys and
+// values, but each of these API functions (of the flutter/SharedPreferences
+// version we started with) made an atomic update to the settings and thus we
+// couldn't update multiple values and then commit then all at once
+// atomically. Since Settings V1 we store everything in just one
+// SharedPreferences key, the `atomicSharedPrefsSettingsKey`, and save/commit
+// the whole thing atomically at once.
+const String atomicSharedPrefsSettingsKey = "settings";
diff --git a/lib/app/utils/settings/settings.dart b/lib/app/utils/settings/settings.dart
index 785abcd1..ad74839f 100644
--- a/lib/app/utils/settings/settings.dart
+++ b/lib/app/utils/settings/settings.dart
@@ -5,22 +5,33 @@ import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'v1.dart' as v1;
+import 'v2.dart' as v2;
import '../files.dart';
import '../log.dart';
import '../master_key.dart';
-typedef DatabaseId = v1.DatabaseId;
-typedef Settings = v1.Settings;
+typedef DatabaseId = v2.DatabaseId;
+typedef Settings = v2.Settings;
Future loadAndMigrateSettings(Session session) async {
await _migratePaths();
final masterKey = await MasterKey.init();
- final settings = await v1.Settings.init(masterKey);
- await settings.migrate(session);
-
- return settings;
+ try {
+ final settingsV2 = await v2.Settings.init(masterKey);
+ return settingsV2;
+ } on v2.InvalidSettingsVersion catch (e) {
+ if (e.statedVersion < 2) {
+ final settingsV1 = await v1.Settings.init(masterKey);
+ await settingsV1.migrate(session);
+ return await v2.Settings.initWithV1(settingsV1);
+ } else {
+ throw "Settings have been created with a newer Ouisync version and thus can't be migrated";
+ }
+ } catch (e) {
+ rethrow;
+ }
}
Future _migratePaths() async {
diff --git a/lib/app/utils/settings/v1.dart b/lib/app/utils/settings/v1.dart
index 34954a34..b13fdea4 100644
--- a/lib/app/utils/settings/v1.dart
+++ b/lib/app/utils/settings/v1.dart
@@ -12,6 +12,7 @@ import '../../models/models.dart';
import '../files.dart';
import '../master_key.dart';
import '../utils.dart';
+import 'atomic_shared_prefs_settings_key.dart';
import 'v0/v0.dart' as v0;
class DatabaseId extends Equatable {
@@ -92,7 +93,7 @@ class SettingsRoot {
int inputVersion = data[_versionKey];
if (inputVersion != version) {
- throw "Invalid settings version ($inputVersion)";
+ throw InvalidSettingsVersion(inputVersion);
}
final repos = {
@@ -116,8 +117,6 @@ class SettingsRoot {
}
class Settings with AppLogger {
- static const String settingsKey = "settings";
-
final MasterKey masterKey;
final SettingsRoot _root;
@@ -128,7 +127,8 @@ class Settings with AppLogger {
Settings._(this._root, this._prefs, this.masterKey);
Future _storeRoot() async {
- await _prefs.setString(settingsKey, json.encode(_root.toJson()));
+ await _prefs.setString(
+ atomicSharedPrefsSettingsKey, json.encode(_root.toJson()));
}
static Future init(
@@ -136,7 +136,7 @@ class Settings with AppLogger {
) async {
final prefs = await SharedPreferences.getInstance();
- final json = prefs.getString(settingsKey);
+ final json = prefs.getString(atomicSharedPrefsSettingsKey);
final root = SettingsRoot.fromJson(json);
return Settings._(root, prefs, masterKey);
@@ -148,8 +148,10 @@ class Settings with AppLogger {
}
Future _migrateValues(Session session) async {
- // Check if already fully migrated.
- if (_prefs.containsKey(settingsKey) && _prefs.getKeys().length == 1) {
+ // Check if already fully migrated. The `atomicSharedPrefsSettingsKey` was introduced in V1
+ // where it's the only key.
+ if (_prefs.containsKey(atomicSharedPrefsSettingsKey) &&
+ _prefs.getKeys().length == 1) {
return;
}
@@ -234,7 +236,8 @@ class Settings with AppLogger {
// to do this **after** we've stored the root and version number of this
// settings.
for (final key in _prefs.getKeys()) {
- if (key == settingsKey || key == v0.Settings.knownRepositoriesKey) {
+ if (key == atomicSharedPrefsSettingsKey ||
+ key == v0.Settings.knownRepositoriesKey) {
continue;
}
@@ -442,4 +445,49 @@ class Settings with AppLogger {
}
print("=======================================");
}
+
+ //------------------------------------------------------------------
+
+ // Only for use in migrations!
+ MigrationContext getMigrationContext() => MigrationContext(
+ masterKey: masterKey,
+ acceptedEqualitieValues: _root.acceptedEqualitieValues,
+ showOnboarding: _root.showOnboarding,
+ highestSeenProtocolNumber: _root.highestSeenProtocolNumber,
+ defaultRepo: _root.defaultRepo?.clone(),
+ repos: Map.from(_root.repos),
+ defaultRepositoriesDirVersion: _root.defaultRepositoriesDirVersion,
+ sharedPreferences: _prefs,
+ );
+}
+
+class InvalidSettingsVersion {
+ int statedVersion;
+ InvalidSettingsVersion(this.statedVersion);
+ @override
+ String toString() => "Invalid settings version ($statedVersion)";
+}
+
+class MigrationContext {
+ final MasterKey masterKey;
+ final bool acceptedEqualitieValues;
+ final bool showOnboarding;
+ // Intentionally not including this one as it's not used in V2.
+ //final bool enableSyncOnMobileInternet;
+ final int? highestSeenProtocolNumber;
+ final RepoLocation? defaultRepo;
+ final Map repos;
+ final int defaultRepositoriesDirVersion;
+ final SharedPreferences sharedPreferences;
+
+ MigrationContext({
+ required this.masterKey,
+ required this.acceptedEqualitieValues,
+ required this.showOnboarding,
+ required this.highestSeenProtocolNumber,
+ required this.defaultRepo,
+ required this.repos,
+ required this.defaultRepositoriesDirVersion,
+ required this.sharedPreferences,
+ });
}
diff --git a/lib/app/utils/settings/v2.dart b/lib/app/utils/settings/v2.dart
new file mode 100644
index 00000000..2364c878
--- /dev/null
+++ b/lib/app/utils/settings/v2.dart
@@ -0,0 +1,277 @@
+import 'dart:convert';
+import 'dart:io' as io;
+
+import 'package:equatable/equatable.dart';
+import 'package:ouisync/ouisync.dart';
+import 'package:path/path.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+import '../../models/models.dart';
+import '../files.dart';
+import '../master_key.dart';
+import '../utils.dart';
+import 'atomic_shared_prefs_settings_key.dart';
+import 'v1.dart' as v1;
+
+typedef DatabaseId = v1.DatabaseId;
+
+//--------------------------------------------------------------------
+
+class SettingsRoot {
+ static const int version = 2;
+
+ static const _versionKey = 'version';
+ static const _acceptedEqualitieValuesKey = 'acceptedEqualitieValues';
+ static const _showOnboardingKey = 'showOnboarding';
+ static const _enableSyncOnMobileInternetKey = 'enableSyncOnMobileInternet';
+ static const _highestSeenProtocolNumberKey = 'highestSeenProtocolNumber';
+ static const _defaultRepoKey = 'defaultRepo';
+ static const _reposKey = 'repos';
+ static const _defaultRepositoriesDirVersionKey =
+ 'defaultRepositoriesDirVersion';
+
+ // Did the user accept the eQ values?
+ bool acceptedEqualitieValues = false;
+ // Show onboarding (will flip to false once shown).
+ bool showOnboarding = true;
+ bool enableSyncOnMobileInternet = false;
+ int? highestSeenProtocolNumber;
+ // NOTE: In order to preserve plausible deniability, once the current repo is
+ // locked in _AuthModeBlindOrManual, this value must be set to `null`.
+ RepoLocation? defaultRepo;
+ Map repos = {};
+
+ // Whenever we change the default repos path, increment this value and implement a migration.
+ int defaultRepositoriesDirVersion = 1;
+
+ SettingsRoot._();
+
+ SettingsRoot({
+ required this.acceptedEqualitieValues,
+ required this.showOnboarding,
+ required this.enableSyncOnMobileInternet,
+ required this.highestSeenProtocolNumber,
+ required this.defaultRepo,
+ required this.repos,
+ required this.defaultRepositoriesDirVersion,
+ });
+
+ Map toJson() {
+ final r = {
+ _versionKey: version,
+ _acceptedEqualitieValuesKey: acceptedEqualitieValues,
+ _showOnboardingKey: showOnboarding,
+ _enableSyncOnMobileInternetKey: enableSyncOnMobileInternet,
+ _highestSeenProtocolNumberKey: highestSeenProtocolNumber,
+ _defaultRepoKey: defaultRepo?.path,
+ _reposKey: {
+ for (var kv in repos.entries) kv.key.toString(): kv.value.path
+ },
+ _defaultRepositoriesDirVersionKey: defaultRepositoriesDirVersion,
+ };
+ return r;
+ }
+
+ factory SettingsRoot.fromJson(String? s) {
+ if (s == null) {
+ return SettingsRoot._();
+ }
+
+ final data = json.decode(s);
+
+ int inputVersion = data[_versionKey];
+
+ if (inputVersion != version) {
+ throw InvalidSettingsVersion(inputVersion);
+ }
+
+ final repos = {
+ for (var kv in data[_reposKey]!.entries)
+ DatabaseId(kv.key): RepoLocation.fromDbPath(kv.value)
+ };
+
+ String? defaultRepo = data[_defaultRepoKey];
+
+ return SettingsRoot(
+ acceptedEqualitieValues: data[_acceptedEqualitieValuesKey]!,
+ showOnboarding: data[_showOnboardingKey]!,
+ enableSyncOnMobileInternet: data[_enableSyncOnMobileInternetKey]!,
+ highestSeenProtocolNumber: data[_highestSeenProtocolNumberKey],
+ defaultRepo: defaultRepo?.let((path) => RepoLocation.fromDbPath(path)),
+ repos: repos,
+ defaultRepositoriesDirVersion:
+ data[_defaultRepositoriesDirVersionKey] ?? 0,
+ );
+ }
+}
+
+class Settings with AppLogger {
+ final MasterKey masterKey;
+
+ final SettingsRoot _root;
+ final SharedPreferences _prefs;
+
+ //------------------------------------------------------------------
+
+ Settings._(this._root, this._prefs, this.masterKey);
+
+ Future _storeRoot() async {
+ await _prefs.setString(
+ atomicSharedPrefsSettingsKey, json.encode(_root.toJson()));
+ }
+
+ static Future init(
+ MasterKey masterKey,
+ ) async {
+ final prefs = await SharedPreferences.getInstance();
+
+ final json = prefs.getString(atomicSharedPrefsSettingsKey);
+
+ // The `atomicSharedPrefsSettingsKey` was introduced in V1 where it's the
+ // only key. The other condition is to ensure this is not a freshly
+ // generated `SharedPreferences` instance.
+ if (json == null && prefs.getKeys().length != 0) {
+ throw InvalidSettingsVersion(0);
+ }
+
+ final root = SettingsRoot.fromJson(json);
+
+ return Settings._(root, prefs, masterKey);
+ }
+
+ static Future initWithV1(v1.Settings settingsV1) async {
+ final v1 = settingsV1.getMigrationContext();
+
+ final root = SettingsRoot(
+ acceptedEqualitieValues: v1.acceptedEqualitieValues,
+ showOnboarding: v1.showOnboarding,
+ // This is what changed from V1 to V2, by default it's now `false`.
+ enableSyncOnMobileInternet: false,
+ highestSeenProtocolNumber: v1.highestSeenProtocolNumber,
+ defaultRepo: v1.defaultRepo,
+ repos: v1.repos,
+ defaultRepositoriesDirVersion: v1.defaultRepositoriesDirVersion,
+ );
+
+ final settingsV2 = Settings._(root, v1.sharedPreferences, v1.masterKey);
+
+ await settingsV2._storeRoot();
+
+ return settingsV2;
+ }
+
+ //------------------------------------------------------------------
+
+ bool getEqualitieValues() => _root.acceptedEqualitieValues;
+
+ Future setEqualitieValues(bool value) async {
+ _root.acceptedEqualitieValues = value;
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+
+ bool getShowOnboarding() => _root.showOnboarding;
+
+ Future setShowOnboarding(bool value) async {
+ _root.showOnboarding = value;
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+
+ bool getSyncOnMobileEnabled() => _root.enableSyncOnMobileInternet;
+
+ Future setSyncOnMobileEnabled(bool enable) async {
+ _root.enableSyncOnMobileInternet = enable;
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+
+ int? getHighestSeenProtocolNumber() => _root.highestSeenProtocolNumber;
+
+ Future setHighestSeenProtocolNumber(int number) async {
+ _root.highestSeenProtocolNumber = number;
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+
+ Iterable get repos => _root.repos.values;
+
+ //------------------------------------------------------------------
+
+ RepoLocation? getRepoLocation(DatabaseId repoId) => _root.repos[repoId];
+
+ Future setRepoLocation(DatabaseId repoId, RepoLocation location) async {
+ _root.repos[repoId] = location;
+ await _storeRoot();
+ }
+
+ DatabaseId? findRepoByLocation(RepoLocation location) => _root.repos.entries
+ .where((entry) => entry.value == location)
+ .map((entry) => entry.key)
+ .firstOrNull;
+
+ Future renameRepo(
+ DatabaseId repoId,
+ RepoLocation newLocation,
+ ) async {
+ if (findRepoByLocation(newLocation) != null) {
+ throw 'Failed to rename repo: "${newLocation.path}" already exists';
+ }
+
+ await setRepoLocation(repoId, newLocation);
+ }
+
+ //------------------------------------------------------------------
+
+ RepoLocation? get defaultRepo => _root.defaultRepo;
+
+ Future setDefaultRepo(RepoLocation? location) async {
+ if (location == _root.defaultRepo) return;
+ _root.defaultRepo = location;
+
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+
+ Future forgetRepo(DatabaseId databaseId) async {
+ final location = _root.repos.remove(databaseId);
+
+ if (_root.defaultRepo == location) {
+ _root.defaultRepo = null;
+ }
+
+ await _storeRoot();
+ }
+
+ //------------------------------------------------------------------
+ Future getDefaultRepositoriesDir() async {
+ final baseDir =
+ (io.Platform.isAndroid ? await getExternalStorageDirectory() : null) ??
+ await getApplicationSupportDirectory();
+ return io.Directory(join(baseDir.path, Constants.folderRepositoriesName));
+ }
+
+ //------------------------------------------------------------------
+
+ void debugPrint() {
+ print("============== Settings ===============");
+ for (final kv in _root.repos.entries) {
+ print("=== ${kv.key}");
+ }
+ print("=======================================");
+ }
+}
+
+class InvalidSettingsVersion {
+ int statedVersion;
+ InvalidSettingsVersion(this.statedVersion);
+ @override
+ String toString() => "Invalid settings version ($statedVersion)";
+}
From 79c1c69c095b64ef54041dd5d690e2b9b128868d Mon Sep 17 00:00:00 2001
From: Peter Jankuliak
Date: Fri, 20 Sep 2024 15:17:43 +0200
Subject: [PATCH 2/5] Remove unused `settings` argument to RepoCubit.create
---
lib/app/cubits/repo.dart | 1 -
lib/app/cubits/repos.dart | 3 ---
test/unit/move_entry_between_repos_test.dart | 5 -----
3 files changed, 9 deletions(-)
diff --git a/lib/app/cubits/repo.dart b/lib/app/cubits/repo.dart
index 63753597..feb5d6c7 100644
--- a/lib/app/cubits/repo.dart
+++ b/lib/app/cubits/repo.dart
@@ -130,7 +130,6 @@ class RepoCubit extends Cubit with AppLogger {
static Future create({
required NativeChannels nativeChannels,
- required Settings settings,
required Repository repo,
required RepoLocation location,
required NavigationCubit navigation,
diff --git a/lib/app/cubits/repos.dart b/lib/app/cubits/repos.dart
index a4bbda68..68c035b5 100644
--- a/lib/app/cubits/repos.dart
+++ b/lib/app/cubits/repos.dart
@@ -287,7 +287,6 @@ class ReposCubit extends WatchSelf with AppLogger {
final cubit = await RepoCubit.create(
nativeChannels: _nativeChannels,
- settings: _settings,
navigation: navigation,
bottomSheet: bottomSheet,
repo: repo,
@@ -479,7 +478,6 @@ class ReposCubit extends WatchSelf with AppLogger {
final cubit = await RepoCubit.create(
nativeChannels: _nativeChannels,
- settings: _settings,
navigation: navigation,
bottomSheet: bottomSheet,
repo: repo,
@@ -564,7 +562,6 @@ class ReposCubit extends WatchSelf with AppLogger {
final cubit = await RepoCubit.create(
nativeChannels: _nativeChannels,
- settings: _settings,
navigation: navigation,
bottomSheet: bottomSheet,
repo: repo,
diff --git a/test/unit/move_entry_between_repos_test.dart b/test/unit/move_entry_between_repos_test.dart
index 36a0246a..145e841c 100644
--- a/test/unit/move_entry_between_repos_test.dart
+++ b/test/unit/move_entry_between_repos_test.dart
@@ -8,7 +8,6 @@ import 'package:ouisync_app/app/models/repo_location.dart';
import 'package:ouisync_app/app/utils/cache_servers.dart';
import 'package:ouisync_app/app/utils/master_key.dart';
import 'package:ouisync_app/app/utils/mounter.dart';
-import 'package:ouisync_app/app/utils/settings/settings.dart';
import 'package:ouisync/native_channels.dart';
import 'package:ouisync/ouisync.dart';
import 'package:path/path.dart' as p;
@@ -26,7 +25,6 @@ void main() {
late RepoCubit otherRepoCubit;
late NativeChannels nativeChannels;
- late Settings settings;
late NavigationCubit navigationCubit;
late EntryBottomSheetCubit bottomSheetCubit;
@@ -59,7 +57,6 @@ void main() {
FlutterSecureStorage.setMockInitialValues({});
SharedPreferences.setMockInitialValues({});
final key = MasterKey.random();
- settings = await Settings.init(key);
navigationCubit = NavigationCubit();
bottomSheetCubit = EntryBottomSheetCubit();
@@ -67,7 +64,6 @@ void main() {
originRepoCubit = await RepoCubit.create(
nativeChannels: nativeChannels,
- settings: settings,
repo: originRepo,
location: locationOrigin,
navigation: navigationCubit,
@@ -78,7 +74,6 @@ void main() {
otherRepoCubit = await RepoCubit.create(
nativeChannels: nativeChannels,
- settings: settings,
repo: otherRepo,
location: locationOther,
navigation: navigationCubit,
From 58bcc6b9e0da329a96d56672cac962102102e838 Mon Sep 17 00:00:00 2001
From: Peter Jankuliak
Date: Fri, 20 Sep 2024 15:18:09 +0200
Subject: [PATCH 3/5] Add "settings migration v1 to v2" test
---
test/unit/settings_test.dart | 67 +++++++++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/test/unit/settings_test.dart b/test/unit/settings_test.dart
index f53e8c1f..87c4aa37 100644
--- a/test/unit/settings_test.dart
+++ b/test/unit/settings_test.dart
@@ -2,6 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:ouisync_app/app/models/auth_mode.dart';
import 'package:ouisync_app/app/utils/utils.dart';
import 'package:ouisync_app/app/utils/settings/v0/v0.dart' as v0;
+import 'package:ouisync_app/app/utils/settings/v1.dart' as v1;
import 'package:ouisync_app/app/models/repo_location.dart';
import 'package:ouisync_app/app/utils/master_key.dart';
import 'package:ouisync/ouisync.dart' show Repository, Session, SessionKind;
@@ -12,7 +13,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// Run with `flutter test test/settings_test.dart`.
void main() {
- test('settings migration', () async {
+ test('settings migration v0 to v1', () async {
SharedPreferences.setMockInitialValues({});
FlutterSecureStorage.setMockInitialValues({});
@@ -76,6 +77,70 @@ void main() {
);
});
+ test('settings migration v1 to v2', () async {
+ SharedPreferences.setMockInitialValues({});
+ FlutterSecureStorage.setMockInitialValues({});
+
+ final prefs = await SharedPreferences.getInstance();
+ expect(prefs.getKeys().isEmpty, true);
+
+ final baseDir = await getApplicationSupportDirectory();
+
+ final fooPath = join(baseDir.path, 'foo.db');
+ final barPath = join(baseDir.path, 'bar.db');
+
+ final masterKey = MasterKey.random();
+ final s1 = await v1.Settings.init(masterKey);
+
+ final repoLocation = RepoLocation.fromDbPath(fooPath);
+
+ await s1.setEqualitieValues(true);
+ await s1.setShowOnboarding(false);
+ await s1.setSyncOnMobileEnabled(true);
+ await s1.setHighestSeenProtocolNumber(1);
+ await s1.setRepoLocation(DatabaseId('123'), repoLocation);
+ await s1.setDefaultRepo(repoLocation);
+
+ final session = Session.create(
+ configPath: join(baseDir.path, 'config'),
+ kind: SessionKind.unique,
+ );
+
+ await Repository.create(
+ session,
+ store: fooPath,
+ readSecret: null,
+ writeSecret: null,
+ );
+
+ final s2 = await loadAndMigrateSettings(session);
+
+ expect(s2.getSyncOnMobileEnabled(), false);
+
+ await prefs.reload();
+
+ // In version 1 we only expect the "settings" value to be present.
+ expect(prefs.getKeys().length, 1);
+ expect(s2.repos, unorderedEquals([RepoLocation.fromDbPath(fooPath)]));
+
+ // The auth mode should have been transfered to the repo metadata
+ final repo = await Repository.open(session, store: fooPath);
+ expect(await repo.getAuthMode(), isA());
+
+ await s2.setRepoLocation(
+ DatabaseId("234"),
+ RepoLocation.fromDbPath(barPath),
+ );
+
+ expect(
+ s2.repos,
+ unorderedEquals([
+ RepoLocation.fromDbPath(fooPath),
+ RepoLocation.fromDbPath(barPath),
+ ]),
+ );
+ });
+
test('master key', () async {
FlutterSecureStorage.setMockInitialValues({});
From eb4466773ccc7edfe648e4af0d931007a6e193a2 Mon Sep 17 00:00:00 2001
From: Peter Jankuliak
Date: Mon, 23 Sep 2024 10:43:02 +0200
Subject: [PATCH 4/5] Fix connectivityType not changing on syncOnMobile change
---
lib/app/cubits/power_control.dart | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/lib/app/cubits/power_control.dart b/lib/app/cubits/power_control.dart
index deb2f517..941590e9 100644
--- a/lib/app/cubits/power_control.dart
+++ b/lib/app/cubits/power_control.dart
@@ -74,6 +74,10 @@ class PowerControlState {
return S.current.messageNetworkIsUnavailable;
}
}
+
+ @override
+ String toString() =>
+ "PowerControlState($connectivityType, $networkMode, syncOnMobile:$syncOnMobile, ...)";
}
class PowerControl extends Cubit with AppLogger {
@@ -112,10 +116,8 @@ class PowerControl extends Cubit with AppLogger {
return;
}
- emitUnlessClosed(state.copyWith(syncOnMobile: value));
-
await _settings.setSyncOnMobileEnabled(value);
- await _refresh();
+ await _refresh(syncOnMobile: value);
}
Future setPortForwardingEnabled(bool value) async {
@@ -149,8 +151,12 @@ class PowerControl extends Cubit with AppLogger {
}
}
- Future _onConnectivityChange(ConnectivityResult result) async {
- if (result == state.connectivityType) {
+ Future _onConnectivityChange(ConnectivityResult result,
+ {bool? syncOnMobile = null}) async {
+ syncOnMobile ??= state.syncOnMobile;
+
+ if (result == state.connectivityType &&
+ syncOnMobile == state.syncOnMobile) {
// The Cubit/Bloc machinery knows not to rebuild widgets if the state
// doesn't change, but in this function we also call
// `_session.bindNetwork` which we don't necessarily want to do if the
@@ -164,7 +170,7 @@ class PowerControl extends Cubit with AppLogger {
loggy.app('Connectivity event: ${result.name}');
- emit(state.copyWith(connectivityType: result));
+ emit(state.copyWith(connectivityType: result, syncOnMobile: syncOnMobile));
var newMode = NetworkMode.disabled;
@@ -197,8 +203,9 @@ class PowerControl extends Cubit with AppLogger {
await _setNetworkMode(newMode);
}
- Future _refresh() async =>
- _onConnectivityChange((await _connectivity.checkConnectivity()).last);
+ Future _refresh({bool? syncOnMobile = null}) async =>
+ _onConnectivityChange((await _connectivity.checkConnectivity()).last,
+ syncOnMobile: syncOnMobile);
Future _setNetworkMode(NetworkMode mode, {force = false}) async {
if (state.networkMode == null || mode != state.networkMode || force) {
From beb07909cd2b4037b1d6955b32123c4de2a0ac3c Mon Sep 17 00:00:00 2001
From: Peter Jankuliak
Date: Tue, 24 Sep 2024 13:27:12 +0200
Subject: [PATCH 5/5] Update ouisync
- Disallow outgoing connections on restricted connectivity
- Fix a typo in network/gateway.rs
- Add RUST_LOG instructions to README.md
---
ouisync | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ouisync b/ouisync
index 05851e53..e31d2c37 160000
--- a/ouisync
+++ b/ouisync
@@ -1 +1 @@
-Subproject commit 05851e535ff69dd1e15664e223dd3fafc3adf5ce
+Subproject commit e31d2c37a9491b16cae4499b8fe9756558bc36b6