mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 04:34:55 -06:00
[BITAU-126] Add option in Settings to Open Bitwarden App (#170)
This commit is contained in:
parent
f37414702d
commit
23150d48e6
@ -37,7 +37,7 @@ enum ExternalLinksConstants {
|
||||
static let passwordManagerScheme = URL(string: "bitwarden://")!
|
||||
|
||||
/// A deeplink used by the password manager app to open the options menu.
|
||||
static let passwordManagerSettings = URL(string: "bitwarden://sync_authenticator?options=true")!
|
||||
static let passwordManagerSettings = URL(string: "bitwarden://settings/account_security")!
|
||||
|
||||
/// A markdown link to Bitwarden's privacy policy.
|
||||
static let privacyPolicy = URL(string: "https://bitwarden.com/privacy/")!
|
||||
|
||||
@ -25,6 +25,9 @@ enum SettingsAction: Equatable {
|
||||
/// The privacy policy button was tapped.
|
||||
case privacyPolicyTapped
|
||||
|
||||
/// The sync with bitwarden app button was tapped.
|
||||
case syncWithBitwardenAppTapped
|
||||
|
||||
/// The toast was shown or hidden.
|
||||
case toastShown(Toast?)
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ final class SettingsProcessor: StateProcessor<SettingsState, SettingsAction, Set
|
||||
// MARK: Types
|
||||
|
||||
typealias Services = HasBiometricsRepository
|
||||
& HasConfigService
|
||||
& HasErrorReporter
|
||||
& HasExportItemsService
|
||||
& HasPasteboardService
|
||||
@ -76,6 +77,8 @@ final class SettingsProcessor: StateProcessor<SettingsState, SettingsAction, Set
|
||||
coordinator.showAlert(.privacyPolicyAlert {
|
||||
self.state.url = ExternalLinksConstants.privacyPolicy
|
||||
})
|
||||
case .syncWithBitwardenAppTapped:
|
||||
state.url = ExternalLinksConstants.passwordManagerSettings
|
||||
case let .toastShown(newValue):
|
||||
state.toast = newValue
|
||||
case .tutorialTapped:
|
||||
@ -114,6 +117,7 @@ final class SettingsProcessor: StateProcessor<SettingsState, SettingsAction, Set
|
||||
state.currentLanguage = services.stateService.appLanguage
|
||||
state.appTheme = await services.stateService.getAppTheme()
|
||||
state.biometricUnlockStatus = await loadBiometricUnlockPreference()
|
||||
state.shouldShowSyncButton = await services.configService.getFeatureFlag(.enablePasswordManagerSync)
|
||||
}
|
||||
|
||||
/// Sets the user's biometric auth
|
||||
|
||||
@ -5,6 +5,7 @@ import XCTest
|
||||
class SettingsProcessorTests: AuthenticatorTestCase {
|
||||
// MARK: Properties
|
||||
|
||||
var configService: MockConfigService!
|
||||
var coordinator: MockCoordinator<SettingsRoute, SettingsEvent>!
|
||||
var subject: SettingsProcessor!
|
||||
|
||||
@ -13,10 +14,13 @@ class SettingsProcessorTests: AuthenticatorTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
configService = MockConfigService()
|
||||
coordinator = MockCoordinator()
|
||||
subject = SettingsProcessor(
|
||||
coordinator: coordinator.asAnyCoordinator(),
|
||||
services: ServiceContainer.withMocks(),
|
||||
services: ServiceContainer.withMocks(
|
||||
configService: configService
|
||||
),
|
||||
state: SettingsState()
|
||||
)
|
||||
}
|
||||
@ -30,6 +34,24 @@ class SettingsProcessorTests: AuthenticatorTestCase {
|
||||
|
||||
// MARK: Tests
|
||||
|
||||
/// Performing `.loadData` with the password manager sync disabled sets
|
||||
/// `state.shouldShowSyncButton` to `false`.
|
||||
func test_perform_loadData_syncDisabled() async throws {
|
||||
configService.featureFlagsBool[.enablePasswordManagerSync] = false
|
||||
await subject.perform(.loadData)
|
||||
|
||||
XCTAssertFalse(subject.state.shouldShowSyncButton)
|
||||
}
|
||||
|
||||
/// Performing `.loadData` with the password manager sync enabled sets
|
||||
/// `state.shouldShowSyncButton` to `true`.
|
||||
func test_perform_loadData_syncEnabled() async throws {
|
||||
configService.featureFlagsBool[.enablePasswordManagerSync] = true
|
||||
await subject.perform(.loadData)
|
||||
|
||||
XCTAssertTrue(subject.state.shouldShowSyncButton)
|
||||
}
|
||||
|
||||
/// Receiving `.backupTapped` shows an alert for the backup information.
|
||||
func test_receive_backupTapped() async throws {
|
||||
subject.receive(.backupTapped)
|
||||
@ -45,4 +67,12 @@ class SettingsProcessorTests: AuthenticatorTestCase {
|
||||
|
||||
XCTAssertEqual(coordinator.routes.last, .exportItems)
|
||||
}
|
||||
|
||||
/// Receiving `.syncWithBitwardenAppTapped` adds the Password Manager settings URL to the state to
|
||||
/// navigate the user to the PM app's settings.
|
||||
func test_receive_syncWithBitwardenAppTapped() {
|
||||
subject.receive(.syncWithBitwardenAppTapped)
|
||||
|
||||
XCTAssertEqual(subject.state.url, ExternalLinksConstants.passwordManagerSettings)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ struct SettingsState: Equatable {
|
||||
/// The current language selection.
|
||||
var currentLanguage: LanguageOption = .default
|
||||
|
||||
/// A flag to indicate if we should show the "Sync with the Bitwarden app" button
|
||||
/// Defaults to false, which indicates we should not show the button.
|
||||
var shouldShowSyncButton = false
|
||||
|
||||
/// A toast message to show in the view.
|
||||
var toast: Toast?
|
||||
|
||||
|
||||
@ -102,9 +102,17 @@ struct SettingsView: View {
|
||||
store.send(.exportItemsTapped)
|
||||
}
|
||||
|
||||
SettingsListItem(Localizations.backup, hasDivider: false) {
|
||||
SettingsListItem(Localizations.backup, hasDivider: store.state.shouldShowSyncButton) {
|
||||
store.send(.backupTapped)
|
||||
}
|
||||
|
||||
if store.state.shouldShowSyncButton {
|
||||
externalLinkRow(
|
||||
Localizations.syncWithTheBitwardenApp,
|
||||
action: .syncWithBitwardenAppTapped,
|
||||
hasDivider: false
|
||||
)
|
||||
}
|
||||
}
|
||||
.cornerRadius(10)
|
||||
}
|
||||
|
||||
@ -70,6 +70,14 @@ class SettingsViewTests: AuthenticatorTestCase {
|
||||
XCTAssertEqual(processor.dispatchedActions.last, .privacyPolicyTapped)
|
||||
}
|
||||
|
||||
/// Tapping the sync with Bitwarden app button dispatches the `.syncWithBitwardenAppTapped` action.
|
||||
func test_syncWithBitwardenButton_tap() throws {
|
||||
processor.state.shouldShowSyncButton = true
|
||||
let button = try subject.inspect().find(button: Localizations.syncWithTheBitwardenApp)
|
||||
try button.tap()
|
||||
XCTAssertEqual(processor.dispatchedActions.last, .syncWithBitwardenAppTapped)
|
||||
}
|
||||
|
||||
/// Tapping the tutorial button dispatches the `.tutorialTapped` action.
|
||||
func test_tutorialButton_tap() throws {
|
||||
let button = try subject.inspect().find(button: Localizations.launchTutorial)
|
||||
@ -91,4 +99,13 @@ class SettingsViewTests: AuthenticatorTestCase {
|
||||
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5]
|
||||
)
|
||||
}
|
||||
|
||||
/// Tests the view renders correctly with the `shouldShowSyncButton` set to `true`.
|
||||
func test_viewRenderWithSyncRow() {
|
||||
processor.state.shouldShowSyncButton = true
|
||||
assertSnapshots(
|
||||
of: subject,
|
||||
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
@ -13,6 +13,7 @@ final class SettingsCoordinator: Coordinator, HasStackNavigator {
|
||||
|
||||
typealias Services = HasBiometricsRepository
|
||||
& HasCameraService
|
||||
& HasConfigService
|
||||
& HasErrorReporter
|
||||
& HasExportItemsService
|
||||
& HasImportItemsService
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user