Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions nym-vpn-apple/Home/Sources/Gateways/GatewaysView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,14 @@ private extension GatewaysView {
hopType: viewModel.type,
country: usCountry,
region: region,
servers: viewModel.gatewayManager.vpn.filter { $0.location?.region == region},
servers: viewModel.gatewayManager.vpn.filter { $0.location?.region == region },
infoButtonTapCompletion: { _ in },
path: $viewModel.path,
entryGateway: $viewModel.connectionManager.entryGateway,
exitRouter: $viewModel.connectionManager.exitRouter,
scrollToModel: .constant(.empty)
)
}

}
}
}
101 changes: 59 additions & 42 deletions nym-vpn-apple/Home/Sources/Gateways/GatewaysViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ import UIComponents

@MainActor extension GatewaysViewModel {
func gatewaysInCountry(with countryCode: String) -> [GatewayNode] {
gateways.filter { $0.location?.twoLetterIsoCountryCode == countryCode }
gateways.filter {
$0.location?.twoLetterIsoCountryCode.caseInsensitiveCompare(countryCode) == .orderedSame
}
}
}

Expand All @@ -75,54 +77,69 @@ import UIComponents
// MARK: - Gateways -
@MainActor private extension GatewaysViewModel {
func updateGateways() {
switch connectionManager.connectionType {
case .mixnet5hop:
switch type {
case .entry:
gateways = gatewayManager.entry
case .exit:
gateways = gatewayManager.exit
Task { [weak self] in
guard let self else { return }
switch connectionManager.connectionType {
case .mixnet5hop:
switch type {
case .entry:
gateways = gatewayManager.entry
case .exit:
gateways = gatewayManager.exit
}
case .wireguard:
gateways = gatewayManager.vpn
}
case .wireguard:
gateways = gatewayManager.vpn
}
countries = Array(Set(gateways.map { $0.location?.twoLetterIsoCountryCode }))
.compactMap { gatewayManager.localizedCountry(with: $0) }
.sorted {
$0.name.compare(
$1.name,
options: [.caseInsensitive, .diacriticInsensitive, .widthInsensitive],
range: nil,
locale: Locale.current
) == .orderedAscending
let result = Array(Set(gateways.map { $0.location?.twoLetterIsoCountryCode }))
.compactMap { self.gatewayManager.localizedCountry(with: $0) }
.sorted {
$0.name.compare(
$1.name,
options: [.caseInsensitive, .diacriticInsensitive, .widthInsensitive],
range: nil,
locale: Locale.current
) == .orderedAscending
}
await MainActor.run {
self.countries = result
}
}
}

func searchCountriesGateways() {
guard searchText.count >= minimumSearchSymbols
else {
foundCountries = [Country]()
foundGateways = [GatewayNode]()
return
}
foundCountries = countries.filter {
$0.name.lowercased().localizedCaseInsensitiveContains(searchText.lowercased())
|| $0.code.lowercased().localizedCaseInsensitiveContains(searchText.lowercased())
}

var seen = Set<String>()
foundUSRegions = gateways
.lazy
.filter { $0.location?.twoLetterIsoCountryCode.caseInsensitiveCompare("US") == .orderedSame }
.compactMap { $0.location?.region.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter {
!$0.isEmpty && $0.range(of: self.searchText, options: [.caseInsensitive, .diacriticInsensitive]) != nil
Task { [weak self] in
guard let self, searchText.count >= minimumSearchSymbols
else {
await MainActor.run {
self?.foundCountries = [Country]()
self?.foundGateways = [GatewayNode]()
}
return
}
let newCountries = countries.filter {
$0.name.lowercased().localizedCaseInsensitiveContains(self.searchText.lowercased())
|| $0.code.lowercased().localizedCaseInsensitiveContains(self.searchText.lowercased())
}
.filter { seen.insert($0).inserted }

foundGateways = gateways.filter {
$0.moniker?.lowercased().localizedCaseInsensitiveContains(searchText.lowercased()) ?? false
|| $0.id.lowercased().localizedCaseInsensitiveContains(searchText.lowercased())
var seen = Set<String>()
let newRegions = gateways
.filter { $0.location?.twoLetterIsoCountryCode.caseInsensitiveCompare("US") == .orderedSame }
.compactMap { $0.location?.region.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter {
!$0.isEmpty
&& $0.range(of: self.searchText, options: [.caseInsensitive, .diacriticInsensitive]) != nil
}
.filter { seen.insert($0).inserted }

let newGateways = gateways.filter {
$0.moniker?.lowercased().localizedCaseInsensitiveContains(self.searchText.lowercased()) ?? false
|| $0.id.lowercased().localizedCaseInsensitiveContains(self.searchText.lowercased())
}
await MainActor.run {
self.foundCountries = newCountries
self.foundUSRegions = newRegions
self.foundGateways = newGateways
}
}
}
}
51 changes: 40 additions & 11 deletions nym-vpn-apple/Home/Sources/Welcome/WelcomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ import Theme
let sentryText = "welcome.sentry".localizedString
let continueText = "welcome.continue".localizedString

@Published public private(set) var subtitleAttributed: AttributedString?
@Published public private(set) var privacyPolicyAttributed: AttributedString?

public init(appSettings: AppSettings) {
self.appSettings = appSettings
precomputeAttributedStrings()
}

func subtitleViewHorizontalPadding() -> CGFloat {
Expand Down Expand Up @@ -48,20 +52,45 @@ import Theme
)
}

func subtitleAttributedString() -> AttributedString? {
let options = AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)
return try? AttributedString(
markdown: "\(subtitle1Text) [\(sentryText)](\(Constants.sentryURL.rawValue))\(subtitle2Text)\n\n\(subtitle3Text)",
options: options
)
}

func privacyPolicyAttributedString() -> AttributedString? {
try? AttributedString(markdown: "\(privacyPolicy1Text) [\(termsOfUse)](\(Constants.termsOfUseURL.rawValue)) \(privacyPolicy2Text) [\(privacyPolicy)](\(Constants.privacyPolicyURL.rawValue))")
}
func subtitleAttributedString() -> AttributedString? { subtitleAttributed }
func privacyPolicyAttributedString() -> AttributedString? { privacyPolicyAttributed }

func continueTapped() {
ImpactGenerator.shared.impact()
appSettings.welcomeScreenDidDisplay = true
}
}

private extension WelcomeViewModel {
func precomputeAttributedStrings() {
let s1 = subtitle1Text
let s2 = subtitle2Text
let s3 = subtitle3Text
let sentry = sentryText
let terms = termsOfUse
let pp1 = privacyPolicy1Text
let pp2 = privacyPolicy2Text
let pp = privacyPolicy
let termsURL = Constants.termsOfUseURL.rawValue
let privacyURL = Constants.privacyPolicyURL.rawValue
let sentryURL = Constants.sentryURL.rawValue

Task.detached(priority: .low) { [weak self] in
guard let self else { return }
let options = AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)

let subtitleMarkdown =
"\(s1) [\(sentry)](\(sentryURL))\(s2)\n\n\(s3)"
let subtitle = try? AttributedString(markdown: subtitleMarkdown, options: options)

let privacyMarkdown =
"\(pp1) [\(terms)](\(termsURL)) \(pp2) [\(pp)](\(privacyURL))"
let privacy = try? AttributedString(markdown: privacyMarkdown, options: options)

await MainActor.run {
self.subtitleAttributed = subtitle
self.privacyPolicyAttributed = privacy
}
}
}
}
22 changes: 16 additions & 6 deletions nym-vpn-apple/NymVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@
D9DE89432CFE54CD006C4071 /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = D9DE893C2CFE54CD006C4071 /* [email protected] */; };
D9E552B82CE9E909008C74F6 /* ErrorHandler in Frameworks */ = {isa = PBXBuildFile; productRef = D9E552B72CE9E909008C74F6 /* ErrorHandler */; };
D9E6D8C92BF5FF5F009612CE /* TunnelStatus in Frameworks */ = {isa = PBXBuildFile; productRef = D9E6D8C82BF5FF5F009612CE /* TunnelStatus */; };
D9E9081A2E9F101F00C881AE /* libswift_Concurrency.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D9E908192E9F101F00C881AE /* libswift_Concurrency.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
D9E9081B2E9F134900C881AE /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9CF05D52BA1CBE000489055 /* Network.framework */; };
D9E9F19D2C3806BB0056EB8C /* AutoUpdater in Frameworks */ = {isa = PBXBuildFile; productRef = D9E9F19C2C3806BB0056EB8C /* AutoUpdater */; };
D9E9F1A02C3807A00056EB8C /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = D9E9F19F2C3807A00056EB8C /* Sparkle */; };
D9EACBD62BC016D60094CDDF /* NymVPNUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9EACBD52BC016D60094CDDF /* NymVPNUITests.swift */; };
Expand Down Expand Up @@ -237,6 +239,7 @@
D9DE893B2CFE54CD006C4071 /* launchLogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = launchLogo.png; sourceTree = "<group>"; };
D9DE893C2CFE54CD006C4071 /* [email protected] */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "[email protected]"; sourceTree = "<group>"; };
D9DE893D2CFE54CD006C4071 /* [email protected] */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "[email protected]"; sourceTree = "<group>"; };
D9E908192E9F101F00C881AE /* libswift_Concurrency.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswift_Concurrency.tbd; path = usr/lib/swift/libswift_Concurrency.tbd; sourceTree = SDKROOT; };
D9E9F1A12C3D51650056EB8C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
D9EACBD32BC016D60094CDDF /* NymVPNUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NymVPNUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D9EACBD52BC016D60094CDDF /* NymVPNUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NymVPNUITests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -273,9 +276,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D9E9081A2E9F101F00C881AE /* libswift_Concurrency.tbd in Frameworks */,
D9A1C1292D3E68CF00D33C0F /* NetworkMonitor in Frameworks */,
D93214412B84D29200FFDC5E /* Logging in Frameworks */,
D968BDCB2B6BC98E003F42E5 /* AppVersionProvider in Frameworks */,
D9E9081B2E9F134900C881AE /* Network.framework in Frameworks */,
D98E52312D4005ED00AE92C3 /* ErrorReason in Frameworks */,
D9D381332E7B1683006DAFBB /* FeatureFlagsManager in Frameworks */,
D96740232B56A67A001F6891 /* Tunnels in Frameworks */,
Expand Down Expand Up @@ -439,6 +444,7 @@
D99A14882B35A73F00F2728B /* Frameworks */ = {
isa = PBXGroup;
children = (
D9E908192E9F101F00C881AE /* libswift_Concurrency.tbd */,
D9643C6D2CEBC53A00F6B9A7 /* UserNotifications.framework */,
D9FB33E72BECFD1F009C3419 /* ServiceManagement.framework */,
D990950D2BC6B31900C98E3F /* SystemConfiguration.framework */,
Expand Down Expand Up @@ -913,7 +919,7 @@
CODE_SIGN_ENTITLEMENTS = NymMixnetTunnel/NymMixnetTunnel.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 420;
CURRENT_PROJECT_VERSION = 425;
DEVELOPMENT_TEAM = VW5DZLFHM5;
ENABLE_APP_SANDBOX = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
Expand Down Expand Up @@ -951,7 +957,7 @@
CODE_SIGN_ENTITLEMENTS = NymMixnetTunnel/NymMixnetTunnel.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 420;
CURRENT_PROJECT_VERSION = 425;
DEVELOPMENT_TEAM = VW5DZLFHM5;
ENABLE_APP_SANDBOX = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
Expand Down Expand Up @@ -1115,7 +1121,7 @@
CODE_SIGN_ENTITLEMENTS = NymVPN/NymVPN.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 420;
CURRENT_PROJECT_VERSION = 425;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = VW5DZLFHM5;
ENABLE_APP_SANDBOX = YES;
Expand All @@ -1131,6 +1137,7 @@
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
"$(SDKROOT)/usr/lib/swift",
);
MARKETING_VERSION = 2.11.0;
PRODUCT_BUNDLE_IDENTIFIER = net.nymtech.vpn;
Expand All @@ -1156,7 +1163,7 @@
CODE_SIGN_ENTITLEMENTS = NymVPN/NymVPN.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 420;
CURRENT_PROJECT_VERSION = 425;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = VW5DZLFHM5;
ENABLE_APP_SANDBOX = YES;
Expand All @@ -1172,6 +1179,7 @@
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
"$(SDKROOT)/usr/lib/swift",
);
MARKETING_VERSION = 2.11.0;
PRODUCT_BUNDLE_IDENTIFIER = net.nymtech.vpn;
Expand All @@ -1193,11 +1201,12 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = NymVPNDaemon/Resources/NymVPNDaemon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 122;
CURRENT_PROJECT_VERSION = 127;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"NymVPNDaemon/Preview Content\"";
DEVELOPMENT_TEAM = VW5DZLFHM5;
Expand Down Expand Up @@ -1229,12 +1238,13 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = NymVPNDaemon/Resources/NymVPNDaemon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 122;
CURRENT_PROJECT_VERSION = 127;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"NymVPNDaemon/Preview Content\"";
DEVELOPMENT_TEAM = "";
Expand Down
3 changes: 0 additions & 3 deletions nym-vpn-apple/NymVPN.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion nym-vpn-apple/NymVPN/NymVPNApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import AppSettings
import ConfigurationManager
import ConnectionManager
import CredentialsManager
import ExternalLinkManager
import FeatureFlagsManager
import GatewayManager
import Home
import ImpactGenerator
import Extensions
import KeyboardManager
import MessagesManager
import Migrations
import NetworkMonitor
import NymLogger
import NotificationsManager
import PurchasesManager
Expand Down Expand Up @@ -55,7 +58,6 @@ struct NymVPNApp: App {
}

var body: some Scene {
// swiftlint:disable:next closure_body_length
WindowGroup {
NavigationStack {
// DISABLED until we figure out where the crash is coming from
Expand Down
Loading