Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #7855: Add 'Private tabs' settings section #7868

Merged
merged 7 commits into from
Aug 15, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Combine
import Data
import SnapKit
import BraveUI
import LocalAuthentication

protocol TabTrayDelegate: AnyObject {
/// Notifies the delegate that order of tabs on tab tray has changed.
Expand Down Expand Up @@ -600,7 +601,12 @@ class TabTrayController: AuthenticationController {

@objc func togglePrivateModeAction() {
if !privateMode, Preferences.Privacy.privateBrowsingLock.value {
askForAuthentication(viewType: .tabTray)
askForAuthentication(viewType: .tabTray) { [weak self] success, error in
if !success, error == LAError.passcodeNotSet {
// If Pin code is not set in this device, private mode is enabled default
self?.toggleModeChanger()
}
}
} else {
toggleModeChanger()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,6 @@ struct OtherPrivacySettingsSectionView: View {
})
)
})

if !Preferences.Privacy.privateBrowsingOnly.value {
OptionToggleView(title: Strings.persistentPrivateBrowsing,
subtitle: nil,
option: Preferences.Privacy.persistentPrivateBrowsing) { newValue in
Task { @MainActor in
if newValue {
settings.tabManager.saveAllTabs()
} else {
let tabs = settings.tabManager.allTabs.filter({ $0.isPrivate })
SessionTab.deleteAll(tabIds: tabs.map({ $0.id }))

if !settings.tabManager.privateBrowsingManager.isPrivateBrowsing {
settings.tabManager.willSwitchTabMode(leavingPBM: true)
}
}
}
}
}

ShieldToggleView(
title: Strings.blockMobileAnnoyances,
subtitle: nil,
Expand Down

This file was deleted.

20 changes: 14 additions & 6 deletions Sources/Brave/Frontend/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,19 @@ class SettingsViewController: TableViewController {
}

tabs.rows.append(autoCloseTabsRow)

if !Preferences.Privacy.privateBrowsingOnly.value {
let privateTabsRow = Row(
text: Strings.TabsSettings.privateTabsSettingsTitle,
selection: { [unowned self] in
let vc = UIHostingController(rootView: PrivateTabsView(tabManager: tabManager))
self.navigationController?.pushViewController(vc, animated: true)
},
image: UIImage(braveSystemNamed: "leo.product.private-window"),
accessory: .disclosureIndicator)

tabs.rows.append(privateTabsRow)
}

return tabs
}()
Expand Down Expand Up @@ -594,11 +607,6 @@ class SettingsViewController: TableViewController {
detailText: Strings.Privacy.browserLockDescription,
option: Preferences.Privacy.lockWithPasscode,
image: UIImage(braveSystemNamed: "leo.biometric.login")),
.boolRow(
title: Strings.Privacy.privateBrowsingLock,
detailText: Strings.Privacy.privateBrowsingLockDescription,
option: Preferences.Privacy.privateBrowsingLock,
image: UIImage(braveSystemNamed: "leo.lock")),
Row(
text: Strings.Login.loginListNavigationTitle,
selection: { [unowned self] in
Expand All @@ -607,7 +615,7 @@ class SettingsViewController: TableViewController {
windowProtection: self.windowProtection)
loginsPasswordsViewController.settingsDelegate = self.settingsDelegate
self.navigationController?.pushViewController(loginsPasswordsViewController, animated: true)
}, image: UIImage(braveSystemNamed: "leo.outside"), accessory: .disclosureIndicator),
}, image: UIImage(braveSystemNamed: "leo.outside"), accessory: .disclosureIndicator)
]
)
}()
Expand Down
117 changes: 117 additions & 0 deletions Sources/Brave/Frontend/Settings/Tabs/PrivateTabsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2023 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

import SwiftUI
import BraveStrings
import Preferences
import BraveUI
import Data
import LocalAuthentication

struct PrivateTabsView: View {
enum AuthenticationType {
case faceID, touchID, pinCode, noAuthentication
}

@ObservedObject var privateBrowsingOnly = Preferences.Privacy.privateBrowsingOnly
var tabManager: TabManager?

private var localAuthenticationType: AuthenticationType {
let context = LAContext()

if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
switch context.biometryType {
case .faceID:
return .faceID
case .touchID:
return .touchID
default:
return .noAuthentication
}
}

var error: NSError?
let policyEvaluation = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)

if policyEvaluation {
return .pinCode
}

return .noAuthentication
}

private var browsingLockTitle: String {
var title: String

switch localAuthenticationType {
case .faceID:
title = Strings.TabsSettings.privateBrowsingLockTitleFaceID
case .touchID:
title = Strings.TabsSettings.privateBrowsingLockTitleTouchID
default:
Brandon-T marked this conversation as resolved.
Show resolved Hide resolved
title = Strings.TabsSettings.privateBrowsingLockTitlePinCode
}

return title
}

var body: some View {
Form {
Section(
header: Text(Strings.TabsSettings.privateTabsSettingsTitle.uppercased()),
footer: privateBrowsingOnly.value ? Text("") : Text(Strings.TabsSettings.persistentPrivateBrowsingDescription)) {
if !privateBrowsingOnly.value {
OptionToggleView(title: Strings.TabsSettings.persistentPrivateBrowsingTitle,
subtitle: nil,
option: Preferences.Privacy.persistentPrivateBrowsing) { newValue in
Task { @MainActor in
if newValue {
tabManager?.saveAllTabs()
} else {
if let tabs = tabManager?.allTabs.filter({ $0.isPrivate }) {
SessionTab.deleteAll(tabIds: tabs.map({ $0.id }))
}

if tabManager?.privateBrowsingManager.isPrivateBrowsing == true {
tabManager?.willSwitchTabMode(leavingPBM: true)
}
}
}
}
}

switch localAuthenticationType {
case .faceID, .touchID, .pinCode:
OptionToggleView(title: browsingLockTitle,
subtitle: nil,
option: Preferences.Privacy.privateBrowsingLock)
case .noAuthentication:
Toggle(isOn: .constant(false)) {
VStack(alignment: .leading, spacing: 4) {
Text(browsingLockTitle)
.foregroundColor(Color(.bravePrimary))
}
.opacity(0.25)
}
.disabled(true)
.listRowBackground(Color(.secondaryBraveGroupedBackground))
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
.navigationBarTitle(Strings.TabsSettings.privateTabsSettingsTitle)
.navigationBarTitleDisplayMode(.inline)
.listBackgroundColor(Color(UIColor.braveGroupedBackground))
}
}

#if DEBUG
struct PrivateTabsView_Previews: PreviewProvider {
static var previews: some View {
PrivateTabsView()
}
}
#endif
8 changes: 6 additions & 2 deletions Sources/Brave/Frontend/Widgets/LoadingViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ public class AuthenticationController: LoadingViewController {
}

if !windowProtection.isPassCodeAvailable {
showSetPasscodeError(viewType: viewType) {
if viewType == .tabTray {
completion?(false, LAError.passcodeNotSet)
} else {
showSetPasscodeError(viewType: viewType) {
completion?(false, LAError.passcodeNotSet)
}
}
} else {
windowProtection.presentAuthenticationForViewController(
Expand All @@ -91,7 +95,7 @@ public class AuthenticationController: LoadingViewController {

alert.addAction(
UIAlertAction(title: Strings.OKString, style: .default, handler: { _ in
completion()
completion()
})
)

Expand Down
45 changes: 35 additions & 10 deletions Sources/BraveStrings/BraveStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3340,16 +3340,6 @@ extension Strings {
"BrowserLockDescription", tableName: "BraveShared", bundle: .module,
value: "Unlock Brave with Touch ID, Face ID or system passcode.",
comment: "Description for setting to enable the browser lock privacy feature")
public static let privateBrowsingLock =
NSLocalizedString(
"privacy.private.browsing.lock.title", tableName: "BraveShared", bundle: .module,
value: "Private Browsing Lock",
comment: "Title for setting to enable the private browsing lock privacy feature")
public static let privateBrowsingLockDescription =
NSLocalizedString(
"privacy.private.browsing.lock.description", tableName: "BraveShared", bundle: .module,
value: "Require Passcode to Unlock Private Browsing",
comment: "Description for setting to enable the browser lock privacy feature")
public static let tabTraySetPasscodeAlertDescription =
NSLocalizedString(
"privacy.tab.tray.passcode.alert",
Expand All @@ -3360,6 +3350,41 @@ extension Strings {
}
}

extension Strings {
public struct TabsSettings {
public static let privateTabsSettingsTitle =
NSLocalizedString(
"tabs.settings.privateTabsSettingsTitle", tableName: "BraveShared", bundle: .module,
value: "Private Tabs",
comment: "")
public static let privateBrowsingLockTitleFaceID =
NSLocalizedString(
"tabs.settings.privateBrowsingLockTitleFaceID", tableName: "BraveShared", bundle: .module,
value: "Require Face ID",
comment: "")
public static let privateBrowsingLockTitleTouchID =
NSLocalizedString(
"tabs.settings.privateBrowsingLockTitleTouchID", tableName: "BraveShared", bundle: .module,
value: "Require Touch ID",
comment: "")
public static let privateBrowsingLockTitlePinCode =
NSLocalizedString(
"tabs.settings.privateBrowsingLockTitlePinCode", tableName: "BraveShared", bundle: .module,
value: "Require Pin Code",
comment: "")
public static let persistentPrivateBrowsingTitle =
NSLocalizedString(
"tabs.settings.persistentPrivateBrowsingTitle", tableName: "BraveShared", bundle: .module,
value: "Keep Private Tabs",
comment: "")
public static let persistentPrivateBrowsingDescription =
NSLocalizedString(
"tabs.settings.persistentPrivateBrowsingTitle", tableName: "BraveShared", bundle: .module,
value: "Keep private browsing tabs open when you close the app, ensuring private browsing sessions continue seamlessly.",
comment: "")
}
}

extension Strings {
public struct Login {
public static let loginListEmptyScreenTitle =
Expand Down