0.9.2#64
Merged
Merged
Conversation
iOS: register VPN profile on startup (saveToPreferences), replace deprecated NavigationView with NavigationStack (fixes black top area), shrink StatusRing (lineWidth 10→8, frame 120→96), display Live Quality Score (liveQuality @published + IPC parse + stat cell), fix SplitTunnelView @StateObject→ @ObservedObject singleton bug and toolbar label. macOS: remove duplicate LocalizationManager instance, add applicationWillTerminate cleanup (event monitor + VPN disconnect), guard serverAdaptiveLevel array access with max(0,..), replace deprecated .onReceive(publisher.collect()) with .onChange, fix [weak self] retain cycles in VPNManager, fix helper ping to return real connection state. Android: make PSK nullable in parseConnectionKey, fix split-tunnel hint string duplication. Windows: add TrayManager Drop for clean thread shutdown, add raise_action() CAS-based priority-safe tray action to prevent QUIT→SHOW downgrade. cargo fmt applied across workspace.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[0.9.2] - 2026-06-19
Fixed
loadManager()only calledsaveToPreferenceson the first connect attempt; on a fresh install the profile was never written to the OS VPN list, causing a "Permission denied" error on connect.loadManager()now immediately callssaveToPreferences(withisEnabled = true) on manager creation so the profile exists before the first connection.NavigationViewis deprecated on iOS 16+ and inserts extra vertical spacing when embedded in a tab bar; replaced withNavigationStackin bothContentViewandSplitTunnelView, eliminating the blank black area.lineWidthreduced 10 → 8, frame shrunk 120×120 → 96×96, icon font 36 pt → 28 pt, restoring proper proportions on all iPhone screen sizes.quality_scorefield was already computed and sent by the tunnel process via IPC butVPNManagersilently discarded it andContentViewhad no corresponding UI. Added@Published var liveQuality: InttoVPNManager, parsed from thequality_scoreIPC key infetchTrafficStats(), reset to 0 on disconnect, and displayed as a fourth stat cell (chart.bar.fillicon, green/orange/red colour based on value,—when disconnected).@StateObjectsingleton lifecycle bug —SplitTunnelManager.sharedwas declared with@StateObjectinside aView, causing SwiftUI to create a second independent instance and lose the shared state; changed to@ObservedObject."save_key"("Save") instead of the expected"done"("Done"); corrected.LocalizationManagerinstance —AivpnAppdeclared a separate@StateObject private var localizationalongside theAppDelegate-owned singleton, causing two observers; removed the unused duplicate.applicationWillTerminatewas not implemented; the NSEvent global monitor was leaked and the VPN process left running after the app exited; both are now released inapplicationWillTerminate.serverAdaptiveLevelarray index out-of-bounds —ContentViewindexed["Off","Light","Aggressive","Satellite"][min(vpn.serverAdaptiveLevel, 3)]without guarding against negative values; addedmax(0, ...)to prevent a crash when the server sends an unexpected level byte..onReceive(publisher.collect())for text field filtering — the proxy-portTextFieldused.onReceive(proxyPort.publisher.collect())to filter non-digit input, which was deprecated and fired unreliably; replaced with.onChange(of: proxyPort).disconnect()andpollProxyLog()capturedselfstrongly insideDispatchQueue.main.asyncblocks; changed to[weak self]withguard let self/ optional chaining to prevent leaks when the manager is deallocated during shutdown.pingreturns stale connection state — the ping response always used the initialconnected: falsedefault regardless of whether a client process was actually running; now computed asisConnected && managedPID > 0 && kill(managedPID, 0) == 0.parseConnectionKeyreturnednullwhen the"p"PSK field was absent or blank, rejecting valid connection keys that rely on server-side PSK lookup; changedpsktoString?— connections proceed with a null PSK and the field is passed as empty to the JNI layer.split_tunnel_bypass_countresource.TrayManagerhad noDropimpl; thetray-eventsbackground thread kept pollingMenuEvent/TrayIconEventreceivers afterTrayManagerwas dropped, leaking the thread until process exit. AddedDropimpl that sets ashutdown: AtomicBool; the event loop checks the flag each iteration and exits.tray_event_loopusedaction.store(ACTION_SHOW)unconditionally; a stray icon-click arriving after the user chose Quit from the menu would overwriteACTION_QUITwith the lower-priorityACTION_SHOW, causing the app to show the window instead of exiting. Replaced withraise_action()that performs a CAS loop and only upgrades the action value.Changed
Cargo.toml, iOSApp/Info.plistandTunnel/Info.plist(build 7)."quality"localisation key (EN: "Quality" / RU: "Качество") toLocalizationManager.[0.9.2] — 2026-06-19
Исправлено
loadManager()вызывалsaveToPreferencesтолько при первом подключении; при свежей установке профиль не попадал в список VPN ОС, что вызывало ошибку «Permission denied». ТеперьloadManager()вызываетsaveToPreferences(сisEnabled = true) сразу при создании менеджера.NavigationViewустарел на iOS 16+ и добавлял лишние отступы при встраивании в таб-бар; заменён наNavigationStackвContentViewиSplitTunnelView.lineWidth10 → 8, размер фрейма 120×120 → 96×96, иконка 36 pt → 28 pt — пропорции восстановлены для всех размеров экранов iPhone.quality_scoreуже вычислялось и передавалось туннелем через IPC, ноVPNManagerмолча его игнорировал, аContentViewне имел соответствующего UI. Добавлено@Published var liveQuality: Int, значение парсится из IPC вfetchTrafficStats(), обнуляется при отключении и отображается четвёртой ячейкой статистики (иконкаchart.bar.fill, цвет зелёный/оранжевый/красный по значению,—при отключении).@StateObjectв SplitTunnelView —SplitTunnelManager.sharedобъявлялся через@StateObjectвнутриView, из-за чего SwiftUI создавал второй независимый экземпляр и терял общее состояние; исправлено на@ObservedObject."save_key"(«Сохранить») вместо"done"(«Готово»); исправлено.LocalizationManager—AivpnAppсоздавал отдельный@StateObject private var localizationнаряду с синглтоном изAppDelegate; лишний экземпляр удалён.applicationWillTerminateне был реализован: глобальный NSEvent-монитор утекал, а VPN-процесс продолжал работать после закрытия приложения; теперь оба освобождаются вapplicationWillTerminate.serverAdaptiveLevel—ContentViewиндексировал["Off","Light","Aggressive","Satellite"][min(vpn.serverAdaptiveLevel, 3)]без защиты от отрицательных значений; добавленmax(0, ...)..onReceive(publisher.collect())для фильтрации ввода — текстовое поле порта прокси использовало устаревший и ненадёжный API; заменено на.onChange(of: proxyPort).disconnect()иpollProxyLog()захватывалиselfсильно внутриDispatchQueue.main.async; заменено на[weak self]сguard let self/ опциональной цепочкой.pingвозвращал устаревшее состояние подключения — ответ на ping всегда использовалconnected: falseпо умолчанию; теперь вычисляется какisConnected && managedPID > 0 && kill(managedPID, 0) == 0.parseConnectionKeyвозвращалnullпри отсутствии поля"p", отклоняя валидные ключи с серверным PSK; изменён типpskнаString?— соединение продолжается с пустым PSK.split_tunnel_bypass_count.TrayManagerне было реализацииDrop; потокtray-eventsпродолжал опрашивать события после удаленияTrayManager. ДобавленDrop, устанавливающийshutdown: AtomicBool; цикл событий проверяет флаг и завершается.action.store(ACTION_SHOW)мог перезаписатьACTION_QUITслучайным кликом по иконке; заменено наraise_action()с CAS-циклом, допускающим только повышение приоритета.Изменено
Cargo.tomlворкспейса, iOSApp/Info.plistиTunnel/Info.plist(сборка 7)."quality"(EN: "Quality" / RU: "Качество") вLocalizationManager.