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