fix(session): repair the expiry warning and the gaps the review found

Review of 742f376 (Fable). Three defects made the feature not work for the
case it exists for:

- armExpiryWarning() cancelled the alarm before its own early return. Dart
  re-pushes the deadline every 60 s with no dedupe (rule 93's ">5 s" dedupe
  does not exist), so the first sync after the warning came due deleted the
  armed alarm and never re-armed it. Cancel now happens only on the branch
  that re-arms.
- cancelSessionWarningAlarm() built its PendingIntent with
  FLAG_UPDATE_CURRENT, rewriting the live alarm's RUNTIME_TOKEN to "" so a
  delivery in flight was dropped as a stale generation. It now looks the
  existing PendingIntent up with FLAG_NO_CREATE.
- SOCKS5 mode never requested POST_NOTIFICATIONS (requestPermission() is
  TUN-only because it also raises the VPN consent dialog), so on Android 13+
  both alerts were dropped. New plugin method requests it on its own.

Also:
- the native deadline wins the expiry race against the 1 s Dart tick, so the
  commonest session end reached _onVpnConnectionChanged as a bare tunnel
  drop with no message. It now reconciles and sets the expiry text, with
  postAlert false since the native side already posted.
- a resume reconnect no longer resets the support-ad claim: it re-attaches
  to the same server session, where the top-up has already been spent.
- RevoltAlerts sets setOnlyAlertOnce so a replacing post does not re-sound.
- AGENTS.md rule 93 described a setSessionExpiry/SESSION_EXPIRES_AT_MS path
  that no longer exists, dedupe included. Corrected; that stale text is what
  the warning bug was written against.
- CHANGELOG no longer claims the alert covers the tunnel stopping on its own.

flutter analyze clean; debug APK builds.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
esefxdzandClaude Opus 5 committed 2026-10-17 00:22:26 +03:00
1 parent 742f37683e
commit dd1082c8de
7 files changed
+96 -18

No files matched your search

+12
View File
@@ -224,6 +224,18 @@ class VpnConnection extends ChangeNotifier {
return false;
}
// SOCKS5 mode skips requestPermission below (it would raise the VPN
// consent dialog for an interface it never creates), so ask for the
// notification permission on its own - the session alerts need it.
if (!kIsWeb && _activeMode == ConnectionMode.proxy) {
try {
await _nativeControl.invokeMethod<void>('requestNotificationPermission');
} catch (e) {
debugPrint('[VPN] Notification permission request failed: $e');
}
if (!_isCurrentConnect(connectEpoch)) return false;
}
if (!kIsWeb && _activeMode == ConnectionMode.tun) {
final ok = await _vless.requestPermission();
if (!_isCurrentConnect(connectEpoch)) return false;