chore: release 3.3.0 (sidebar nav fix, notification countdown, hardening)
Co-authored-by: Naveax <[email protected]>
This commit is contained in:
1 parent
4eb1fca666
commit
62104d5730
28 files changed
+670
-215
No files matched your search
@@ -1,9 +1,44 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:revoltvpn/logic/app_colors.dart';
|
||||
|
||||
// A plain bool, not a ValueNotifier: this is only read at the moment of a tap,
|
||||
// so nothing has to react to it changing. Default: false.
|
||||
bool hapticEnabled = false;
|
||||
|
||||
/// Hydrates [hapticEnabled] from disk. Called at boot — the toggle tile below
|
||||
/// only mounts once Settings is opened.
|
||||
Future<void> loadHapticPref() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
hapticEnabled = prefs.getBool('haptic_feedback_enabled') ?? false;
|
||||
}
|
||||
|
||||
/// Haptic calls are deliberately best-effort: a vibration failure must never
|
||||
/// block or fail a VPN connect/disconnect. `mediumImpact` is used instead of
|
||||
/// `lightImpact` — the latter is imperceptible on many Android devices.
|
||||
class Haptics {
|
||||
Haptics._();
|
||||
|
||||
static Future<void> connectionChanged() async {
|
||||
if (!hapticEnabled) return;
|
||||
try {
|
||||
await HapticFeedback.mediumImpact();
|
||||
} catch (e) {
|
||||
debugPrint('[Haptics] Feedback unavailable: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> previewEnabled() async {
|
||||
try {
|
||||
await HapticFeedback.selectionClick();
|
||||
} catch (e) {
|
||||
debugPrint('[Haptics] Preview unavailable: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HapticFeedbackTile extends StatefulWidget {
|
||||
const HapticFeedbackTile({super.key});
|
||||
|
||||
@@ -20,12 +55,9 @@ class _HapticFeedbackTileState extends State<HapticFeedbackTile> {
|
||||
_load();
|
||||
}
|
||||
|
||||
// Loads persisted state and syncs the top-level bool.
|
||||
Future<void> _load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final value = prefs.getBool('haptic_feedback_enabled') ?? false;
|
||||
hapticEnabled = value;
|
||||
if (mounted) setState(() => _on = value);
|
||||
await loadHapticPref();
|
||||
if (mounted) setState(() => _on = hapticEnabled);
|
||||
}
|
||||
|
||||
// Writes to SharedPreferences AND updates the live bool so the connect
|
||||
@@ -34,6 +66,7 @@ class _HapticFeedbackTileState extends State<HapticFeedbackTile> {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('haptic_feedback_enabled', value);
|
||||
hapticEnabled = value;
|
||||
if (value) unawaited(Haptics.previewEnabled());
|
||||
setState(() => _on = value);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ import 'package:revoltvpn/logic/app_colors.dart';
|
||||
|
||||
// ── lib/screens/settings/in_settings/lightning.dart ─────────────────────────
|
||||
// Toggle for the lightning strike overlay on the main screen.
|
||||
//
|
||||
// Same architecture as haptic.dart: exports a top-level bool read synchronously
|
||||
// by lib/components/lightning.dart. Default: true (lightning is on — opt-out).
|
||||
|
||||
// lib/components/lightning.dart reads this in its build method.
|
||||
bool lightningEnabled = true;
|
||||
final ValueNotifier<bool> lightningEnabled = ValueNotifier(true);
|
||||
|
||||
Future<void> loadLightningPref() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
lightningEnabled.value = prefs.getBool('lightning_effect_enabled') ?? true;
|
||||
}
|
||||
|
||||
class LightningToggleTile extends StatefulWidget {
|
||||
const LightningToggleTile({super.key});
|
||||
@@ -28,16 +29,14 @@ class _LightningToggleTileState extends State<LightningToggleTile> {
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final value = prefs.getBool('lightning_effect_enabled') ?? true;
|
||||
lightningEnabled = value;
|
||||
if (mounted) setState(() => _on = value);
|
||||
await loadLightningPref();
|
||||
if (mounted) setState(() => _on = lightningEnabled.value);
|
||||
}
|
||||
|
||||
Future<void> _toggle(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('lightning_effect_enabled', value);
|
||||
lightningEnabled = value;
|
||||
lightningEnabled.value = value;
|
||||
setState(() => _on = value);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ import 'package:revoltvpn/logic/app_colors.dart';
|
||||
|
||||
// ── lib/screens/settings/in_settings/rain.dart ──────────────────────────────
|
||||
// Toggle for the animated rain overlay on the main screen.
|
||||
//
|
||||
// Same architecture as haptic.dart: exports a top-level bool read synchronously
|
||||
// by lib/components/rain.dart. Default: true (rain is on — opt-out).
|
||||
|
||||
// lib/components/rain.dart reads this in its build method.
|
||||
bool rainEnabled = true;
|
||||
final ValueNotifier<bool> rainEnabled = ValueNotifier(true);
|
||||
|
||||
Future<void> loadRainPref() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
rainEnabled.value = prefs.getBool('rain_effect_enabled') ?? true;
|
||||
}
|
||||
|
||||
class RainToggleTile extends StatefulWidget {
|
||||
const RainToggleTile({super.key});
|
||||
@@ -28,16 +29,14 @@ class _RainToggleTileState extends State<RainToggleTile> {
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final value = prefs.getBool('rain_effect_enabled') ?? true;
|
||||
rainEnabled = value;
|
||||
if (mounted) setState(() => _on = value);
|
||||
await loadRainPref();
|
||||
if (mounted) setState(() => _on = rainEnabled.value);
|
||||
}
|
||||
|
||||
Future<void> _toggle(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('rain_effect_enabled', value);
|
||||
rainEnabled = value;
|
||||
rainEnabled.value = value;
|
||||
setState(() => _on = value);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user