Files
umbrix/lib/core/preferences/general_preferences.dart
Umbrix Developer 76a374950f feat: mobile-like window size and always-visible stats
- Changed window size to mobile phone format (400x800)
- Removed width condition for ActiveProxyFooter - now always visible
- Added run-umbrix.sh launch script with icon copying
- Stats cards now display on all screen sizes
2026-01-17 13:09:20 +03:00

181 lines
5.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/foundation.dart';
import 'package:umbrix/core/app_info/app_info_provider.dart';
import 'package:umbrix/core/model/environment.dart';
import 'package:umbrix/core/preferences/actions_at_closing.dart';
// import 'package:umbrix/core/model/region.dart';
import 'package:umbrix/core/preferences/preferences_provider.dart';
import 'package:umbrix/core/utils/preferences_utils.dart';
import 'package:umbrix/features/connection/model/connection_status.dart';
import 'package:umbrix/features/connection/notifier/connection_notifier.dart';
import 'package:umbrix/features/per_app_proxy/model/per_app_proxy_mode.dart';
import 'package:umbrix/features/profile/notifier/active_profile_notifier.dart';
import 'package:umbrix/utils/platform_utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'general_preferences.g.dart';
bool _debugIntroPage = false;
abstract class Preferences {
static final introCompleted = PreferencesNotifier.create(
"intro_completed",
false,
overrideValue: _debugIntroPage && kDebugMode ? false : null,
);
static final silentStart = PreferencesNotifier.create<bool, bool>(
"silent_start",
false,
);
static final disableMemoryLimit = PreferencesNotifier.create<bool, bool>(
"disable_memory_limit",
// disable memory limit on desktop by default
PlatformUtils.isDesktop,
);
static final perAppProxyMode = PreferencesNotifier.create<PerAppProxyMode, String>(
"per_app_proxy_mode",
PerAppProxyMode.exclude,
mapFrom: PerAppProxyMode.values.byName,
mapTo: (value) => value.name,
);
static final markNewProfileActive = PreferencesNotifier.create<bool, bool>(
"mark_new_profile_active",
true,
);
static final dynamicNotification = PreferencesNotifier.create<bool, bool>(
"dynamic_notification",
true,
);
static final autoCheckIp = PreferencesNotifier.create<bool, bool>(
"auto_check_ip",
true,
);
static final startedByUser = PreferencesNotifier.create<bool, bool>(
"started_by_user",
false,
);
static final storeReviewedByUser = PreferencesNotifier.create<bool, bool>(
"store_reviewed_by_user",
false,
);
static final actionAtClose = PreferencesNotifier.create<ActionsAtClosing, String>(
"action_at_close",
ActionsAtClosing.ask,
mapFrom: ActionsAtClosing.values.byName,
mapTo: (value) => value.name,
);
}
@Riverpod(keepAlive: true)
class DebugModeNotifier extends _$DebugModeNotifier {
late final _pref = PreferencesEntry(
preferences: ref.watch(sharedPreferencesProvider).requireValue,
key: "debug_mode",
defaultValue: ref.read(environmentProvider) == Environment.dev,
);
@override
bool build() => _pref.read();
Future<void> update(bool value) {
state = value;
return _pref.write(value);
}
}
@Riverpod(keepAlive: true)
class PerAppProxyList extends _$PerAppProxyList {
late final _include = PreferencesEntry(
preferences: ref.watch(sharedPreferencesProvider).requireValue,
key: "per_app_proxy_include_list",
defaultValue: <String>[],
);
late final _exclude = PreferencesEntry(
preferences: ref.watch(sharedPreferencesProvider).requireValue,
key: "per_app_proxy_exclude_list",
defaultValue: <String>[],
);
@override
List<String> build() {
// Слушаем изменения режима и перестраиваем список
final mode = ref.watch(Preferences.perAppProxyMode);
return mode == PerAppProxyMode.include ? _include.read() : _exclude.read();
}
Future<void> update(List<String> value) async {
print('[PerAppProxyList] update() вызван с ${value.length} приложениями');
final mode = ref.read(Preferences.perAppProxyMode);
print('[PerAppProxyList] Текущий режим: $mode');
// Сначала сохраняем в SharedPreferences
if (mode == PerAppProxyMode.include) {
await _include.write(value);
print('[PerAppProxyList] Записан include список: $value');
} else {
await _exclude.write(value);
print('[PerAppProxyList] Записан exclude список: $value');
}
// Затем обновляем локальное состояние
state = value;
print('[PerAppProxyList] State обновлён');
// Автоматически перезапускаем VPN если он активен
print('[PerAppProxyList] Вызываю _reconnectVpnIfActive()');
await _reconnectVpnIfActive();
}
Future<void> _reconnectVpnIfActive() async {
try {
final connectionNotifier = await ref.read(connectionNotifierProvider.future);
if (connectionNotifier is Connected) {
final profile = await ref.read(activeProfileProvider.future);
await ref.read(connectionNotifierProvider.notifier).reconnect(profile);
}
} catch (_) {
// Игнорируем ошибки если connection provider не инициализирован
}
}
}
@Riverpod(keepAlive: true)
class ExcludedDomainsList extends _$ExcludedDomainsList {
late final _pref = PreferencesEntry(
preferences: ref.watch(sharedPreferencesProvider).requireValue,
key: "excluded_domains_list",
defaultValue: <String>[],
);
@override
List<String> build() => _pref.read();
Future<void> update(List<String> value) async {
state = value;
await _pref.write(value);
// Автоматически перезапускаем VPN если он активен
await _reconnectVpnIfActive();
}
Future<void> _reconnectVpnIfActive() async {
try {
final connectionNotifier = await ref.read(connectionNotifierProvider.future);
if (connectionNotifier is Connected) {
final profile = await ref.read(activeProfileProvider.future);
await ref.read(connectionNotifierProvider.notifier).reconnect(profile);
}
} catch (_) {
// Игнорируем ошибки если connection provider не инициализирован
}
}
}