Skip to content

Commit

Permalink
Add toggling of NetP Notifications to iOS (#2112)
Browse files Browse the repository at this point in the history
Co-authored-by: Sam Symons <[email protected]>
  • Loading branch information
graeme and samsymons authored Oct 31, 2023
1 parent ce8ce75 commit ff096da
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 7 deletions.
34 changes: 34 additions & 0 deletions Core/UserDefaults+NetworkProtection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// UserDefaults+NetworkProtection.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#if NETWORK_PROTECTION

import Foundation

public extension UserDefaults {
static var networkProtectionGroupDefaults: UserDefaults {
let suiteName = "\(Global.groupIdPrefix).netp"
guard let defaults = UserDefaults(suiteName: suiteName) else {
fatalError("Failed to create netP UserDefaults")
}
return defaults
}
}

#endif
30 changes: 29 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,11 @@
EE8594992A44791C008A6D06 /* NetworkProtectionTunnelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */; };
EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE8E56892A56BCE400F11DCA /* NetworkProtection */; };
EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */; };
EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */; };
EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */; };
EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */; };
EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */; };
EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */; };
EEDFE2DA2AC6ED4F00F0E19C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */; };
EEEB80A32A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEEB80A22A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift */; };
EEF0F8CC2ABC832300630031 /* NetworkProtectionDebugFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF0F8CB2ABC832200630031 /* NetworkProtectionDebugFeatures.swift */; };
Expand Down Expand Up @@ -2333,6 +2338,11 @@
EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionNotificationIdentifier.swift; sourceTree = "<group>"; };
EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTunnelController.swift; sourceTree = "<group>"; };
EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNSettingsView.swift; sourceTree = "<group>"; };
EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsView.swift; sourceTree = "<group>"; };
EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = "<group>"; };
EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsViewModel.swift; sourceTree = "<group>"; };
EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsAuthorizationController.swift; sourceTree = "<group>"; };
EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtection.swift"; sourceTree = "<group>"; };
EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Configuration-Alpha.xcconfig"; path = "Configuration/Configuration-Alpha.xcconfig"; sourceTree = "<group>"; };
EEDFE2DB2AC6ED4F00F0E19C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
EEDFE2DD2AC6ED5B00F0E19C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4346,6 +4356,7 @@
children = (
EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */,
EE458D0C2AB1DA4600FC651A /* EventMapping+NetworkProtectionError.swift */,
EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */,
);
name = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -4409,6 +4420,7 @@
isa = PBXGroup;
children = (
EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */,
EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */,
);
name = NetworkProtection;
sourceTree = "<group>";
Expand All @@ -4421,9 +4433,19 @@
name = VPNSettings;
sourceTree = "<group>";
};
EE9D68D62AE1527F00B55EF4 /* VPNNotifications */ = {
isa = PBXGroup;
children = (
EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */,
EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */,
);
name = VPNNotifications;
sourceTree = "<group>";
};
EECD94B22A28B8580085C66E /* NetworkProtection */ = {
isa = PBXGroup;
children = (
EE9D68D62AE1527F00B55EF4 /* VPNNotifications */,
EE9D68CF2AE00CE000B55EF4 /* VPNSettings */,
EE458D122ABB651500FC651A /* Debug */,
EE0153E22A6FE031002A8B26 /* Root */,
Expand Down Expand Up @@ -5098,6 +5120,7 @@
F1DE78591E5CD2A70058895A /* UIViewExtension.swift */,
F1F5337B1F26A9EF00D80D4F /* UserText.swift */,
986DA94924884B18004A7E39 /* WebViewTransition.swift */,
EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */,
);
name = UserInterface;
sourceTree = "<group>";
Expand Down Expand Up @@ -6104,6 +6127,7 @@
F1DE78581E5CAE350058895A /* TabViewGridCell.swift in Sources */,
984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */,
B6BA95C328891E33004ABA20 /* BrowsingMenuAnimator.swift in Sources */,
EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */,
AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */,
8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */,
319A37152829A55F0079FBCE /* AutofillListItemTableViewCell.swift in Sources */,
Expand All @@ -6125,6 +6149,7 @@
1E8AD1C727BE9B2900ABA377 /* DownloadsListDataSource.swift in Sources */,
3157B43527F497F50042D3D7 /* SaveLoginViewController.swift in Sources */,
853C5F6121C277C7001F7A05 /* global.swift in Sources */,
EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */,
F13B4BD31F1822C700814661 /* Tab.swift in Sources */,
F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */,
1EE52ABB28FB1D6300B750C1 /* UIImageExtension.swift in Sources */,
Expand All @@ -6146,6 +6171,7 @@
C1B7B529289420830098FD6A /* RemoteMessaging.xcdatamodeld in Sources */,
986B16C425E92DF0007D23E8 /* BrowsingMenuViewController.swift in Sources */,
988AC355257E47C100793C64 /* RequeryLogic.swift in Sources */,
EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */,
1E4F4A5A297193DE00625985 /* MainViewController+CookiesManaged.swift in Sources */,
8586A10D24CBA7070049720E /* FindInPageActivity.swift in Sources */,
1E1626072968413B0004127F /* ViewExtension.swift in Sources */,
Expand Down Expand Up @@ -6430,6 +6456,7 @@
8586A11024CCCD040049720E /* TabsBarViewController.swift in Sources */,
F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */,
C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */,
EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */,
1E8AD1D727C2E24E00ABA377 /* DownloadsListRowViewModel.swift in Sources */,
C1B0F6422AB08BE9001EAF05 /* MockPrivacyConfiguration.swift in Sources */,
1E865AF0272042DB001C74F3 /* TextSizeSettingsViewController.swift in Sources */,
Expand Down Expand Up @@ -6689,6 +6716,7 @@
CB2A7EF4285383B300885F67 /* AppLastCompiledRulesStore.swift in Sources */,
4B75EA9226A266CB00018634 /* PrintingUserScript.swift in Sources */,
37445F972A155F7C0029F789 /* SyncDataProviders.swift in Sources */,
EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */,
CB258D1F29A52B2500DEBA24 /* Configuration.swift in Sources */,
9847C00027A2DDBB00DB07AA /* AppPrivacyConfigurationDataProvider.swift in Sources */,
F143C3281E4A9A0E00CFDE3A /* StringExtension.swift in Sources */,
Expand Down Expand Up @@ -8968,7 +8996,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 82.0.2;
version = 82.1.0;
};
};
C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit",
"state": {
"branch": null,
"revision": "8768193257dd1f461218ed2a8d7893156bde4bda",
"version": "82.0.2"
"revision": "71e916d070cedcba9ccb3ce9431797260bf5cbea",
"version": "82.1.0"
}
},
{
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGo.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<string>$(GROUP_ID_PREFIX).statistics</string>
<string>$(GROUP_ID_PREFIX).database</string>
<string>$(GROUP_ID_PREFIX).apptp</string>
<string>$(GROUP_ID_PREFIX).netp</string>
</array>
</dict>
</plist>
10 changes: 10 additions & 0 deletions DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,14 @@ extension NetworkProtectionCodeRedemptionCoordinator {
}
}

extension NetworkProtectionVPNNotificationsViewModel {
convenience init() {
let notificationsSettingsStore = NetworkProtectionNotificationsSettingsUserDefaultsStore(userDefaults: .networkProtectionGroupDefaults)
self.init(
notificationsAuthorization: NotificationsAuthorizationController(),
notificationsSettingsStore: notificationsSettingsStore
)
}
}

#endif
9 changes: 6 additions & 3 deletions DuckDuckGo/NetworkProtectionStatusView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ struct NetworkProtectionStatusView: View {
}
settings()
}
.animation(.default, value: statusModel.shouldShowError)
.padding(.top, statusModel.error == nil ? 0 : -20)
.animation(.default, value: statusModel.shouldShowConnectionDetails)
.if(statusModel.animationsOn, transform: {
$0
.animation(.default, value: statusModel.shouldShowConnectionDetails)
.animation(.default, value: statusModel.shouldShowError)
})
.applyInsetGroupedListStyle()
.navigationTitle(UserText.netPNavTitle)
}
Expand Down Expand Up @@ -134,7 +137,7 @@ struct NetworkProtectionStatusView: View {
NavigationLink(UserText.netPVPNSettingsTitle, destination: NetworkProtectionVPNSettingsView())
.font(.system(size: 16))
.foregroundColor(.textPrimary)
NavigationLink(UserText.netPVPNNotificationsTitle, destination: Text("Coming soon!"))
NavigationLink(UserText.netPVPNNotificationsTitle, destination: NetworkProtectionVPNNotificationsView())
.font(.system(size: 16))
.foregroundColor(.textPrimary)
} header: {
Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/NetworkProtectionStatusViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ final class NetworkProtectionStatusViewModel: ObservableObject {
@Published public var location: String?
@Published public var ipAddress: String?

@Published public var animationsOn: Bool = false

public init(tunnelController: TunnelController = NetworkProtectionTunnelController(),
statusObserver: ConnectionStatusObserver = ConnectionStatusObserverThroughSession(),
serverInfoObserver: ConnectionServerInfoObserver = ConnectionServerInfoObserverThroughSession(),
Expand Down Expand Up @@ -166,7 +168,11 @@ final class NetworkProtectionStatusViewModel: ObservableObject {
.store(in: &cancellables)
}

@MainActor
func didToggleNetP(to enabled: Bool) async {
// This is to prevent weird looking animations on navigating to the screen.
// It makes sense as animations should mostly only happen when a user has interacted.
animationsOn = true
if enabled {
await enableNetP()
} else {
Expand Down
90 changes: 90 additions & 0 deletions DuckDuckGo/NetworkProtectionVPNNotificationsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// NetworkProtectionVPNNotificationsView.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#if NETWORK_PROTECTION

import SwiftUI
import UIKit
import NetworkProtection
import Core

@available(iOS 15, *)
struct NetworkProtectionVPNNotificationsView: View {
@StateObject var model = NetworkProtectionVPNNotificationsViewModel()

var body: some View {
List {
switch model.viewKind {
case .loading:
EmptyView()
case .unauthorized:
unauthorizedView
case .authorized:
authorizedView
}
}
.animation(.default, value: model.viewKind)
.applyInsetGroupedListStyle()
.navigationTitle(UserText.netPVPNNotificationsTitle).onAppear {
Task {
await model.onViewAppeared()
}
}
}

@ViewBuilder
private var unauthorizedView: some View {
Section {
Button(UserText.netPTurnOnNotificationsButtonTitle) {
model.turnOnNotifications()
}
.foregroundColor(.controlColor)
} footer: {
Text(UserText.netPTurnOnNotificationsSectionFooter)
.foregroundColor(.textSecondary)
.font(.system(size: 13))
.padding(.top, 6)
}
}

@ViewBuilder
private var authorizedView: some View {
Section {
Toggle(UserText.netPVPNAlertsToggleTitle, isOn: Binding(
get: { model.alertsEnabled },
set: model.didToggleAlerts(to:)
))
.toggleStyle(SwitchToggleStyle(tint: .controlColor))
} footer: {
Text(UserText.netPVPNAlertsToggleSectionFooter)
.foregroundColor(.textSecondary)
.font(.system(size: 13))
.padding(.top, 6)
}
}
}

private extension Color {
static let textPrimary = Color(designSystemColor: .textPrimary)
static let textSecondary = Color(designSystemColor: .textSecondary)
static let cellBackground = Color(designSystemColor: .surface)
static let controlColor = Color(designSystemColor: .accent)
}

#endif
Loading

0 comments on commit ff096da

Please sign in to comment.