Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ public enum PillBadgeStyle {
/// A danger style with red colors.
case danger

/// An info style with blue colors.
case info

/// A success style with green colors.
case success

Expand All @@ -20,6 +23,8 @@ public enum PillBadgeStyle {
switch self {
case .danger:
SharedAsset.Colors.badgeDangerBackground.swiftUIColor
case .info:
SharedAsset.Colors.badgeInfoBackground.swiftUIColor
case .success:
SharedAsset.Colors.badgeSuccessBackground.swiftUIColor
case .warning:
Expand All @@ -32,6 +37,8 @@ public enum PillBadgeStyle {
switch self {
case .danger:
SharedAsset.Colors.badgeDangerBorder.swiftUIColor
case .info:
SharedAsset.Colors.badgeInfoBorder.swiftUIColor
case .success:
SharedAsset.Colors.badgeSuccessBorder.swiftUIColor
case .warning:
Expand All @@ -44,6 +51,8 @@ public enum PillBadgeStyle {
switch self {
case .danger:
SharedAsset.Colors.badgeDangerText.swiftUIColor
case .info:
SharedAsset.Colors.badgeInfoText.swiftUIColor
case .success:
SharedAsset.Colors.badgeSuccessText.swiftUIColor
case .warning:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xF6",
"red" : "0xEE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x55",
"green" : "0x24",
"red" : "0x16"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xDB",
"red" : "0xBE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xDB",
"red" : "0xBE"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xAC",
"green" : "0x41",
"red" : "0x1A"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF6",
"green" : "0xE5",
"red" : "0xDB"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,15 @@
"LineOfCredit" = "Line of credit";
"MoneyMarket" = "Money market";
"Savings" = "Savings";
"Devices" = "Devices";
"NoDevicesFound" = "No devices found";
"CurrentSession" = "Current session";
"PendingRequest" = "Pending request";
"Trusted" = "Trusted";
"RecentlyActiveLabel" = "Recently active:";
"RecentlyActiveX" = "Recently active: %1$@";
"FirstLoginLabel" = "First login:";
"FirstLoginX" = "First login: %1$@";
Comment on lines +1177 to +1180
"Today" = "Today";
"ThisWeek" = "This week";
"LastWeek" = "Last week";
Expand Down
3 changes: 3 additions & 0 deletions BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ extension FeatureFlag: @retroactive CaseIterable {
/// Flag to enable/disable Fill-Assist targeting rules.
static let fillAssistTargetingRules = FeatureFlag(rawValue: "fill-assist-targeting-rules")

/// Feature flag for device management screen.
static let manageDevices = FeatureFlag(rawValue: "pm-4516-manage-devices")

Comment thread
andrebispo5 marked this conversation as resolved.
/// Flag to enable/disable migration from My Vault Items to My Items.
static let migrateMyVaultToMyItems = FeatureFlag(rawValue: "pm-20558-migrate-myvault-to-myitems")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ enum AccountSecurityAction: Equatable {
/// The logout button was pressed.
case logout

/// The manage devices button was tapped.
case manageDevicesTapped

/// The pending login requests button was tapped.
case pendingLoginRequestsTapped

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ final class AccountSecurityProcessor: StateProcessor<// swiftlint:disable:this t
coordinator.navigate(to: .deleteAccount)
case .logout:
showLogoutConfirmation()
case .manageDevicesTapped:
coordinator.navigate(to: .deviceManagement)
case .pendingLoginRequestsTapped:
coordinator.navigate(to: .pendingLoginRequests)
case let .sessionTimeoutActionChanged(newValue):
Expand Down Expand Up @@ -181,6 +183,8 @@ final class AccountSecurityProcessor: StateProcessor<// swiftlint:disable:this t

state.isAuthenticatorSyncEnabled = try await services.stateService.getSyncToAuthenticator()

state.isManageDevicesEnabled = await services.configService.getFeatureFlag(.manageDevices)

if state.biometricUnlockStatus.isEnabled || state.isUnlockWithPINCodeOn {
await completeAccountSetupVaultUnlockIfNeeded()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,13 +828,31 @@ class AccountSecurityProcessorTests: BitwardenTestCase { // swiftlint:disable:th
XCTAssertEqual(coordinator.events.last, .authAction(.logout(userId: nil, userInitiated: true)))
}

/// `.receive(_:)` with `.manageDevicesTapped` navigates to the device management view.
@MainActor
func test_receive_manageDevicesTapped() {
subject.receive(.manageDevicesTapped)
XCTAssertEqual(coordinator.routes.last, .deviceManagement)
}

/// `.receive(_:)` with `.pendingLoginRequestsTapped` navigates to the pending requests view.
@MainActor
func test_receive_pendingLoginRequestsTapped() {
subject.receive(.pendingLoginRequestsTapped)
XCTAssertEqual(coordinator.routes.last, .pendingLoginRequests)
}

/// `perform(.loadData)` sets `isManageDevicesEnabled` from the `manageDevices` feature flag.
@MainActor
func test_perform_loadData_setsIsManageDevicesEnabled() async {
stateService.activeAccount = .fixture()
configService.featureFlagsBool[.manageDevices] = true

await subject.perform(.loadData)

XCTAssertTrue(subject.state.isManageDevicesEnabled)
}

/// `receive(_:)` with `sessionTimeoutActionChanged(:)` presents an alert if `logout` was selected.
/// It then updates the state if `Yes` was tapped on the alert, confirming the user's decision.
@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ struct AccountSecurityState: Equatable {
/// Whether the user has enabled the sync with the authenticator app..
var isAuthenticatorSyncEnabled = false

/// Whether the manage devices feature is enabled.
var isManageDevicesEnabled = false

/// Whether the timeout action policy is in effect.
var isPolicyTimeoutActionEnabled = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ struct AccountSecurityView: View {
VStack(spacing: 16) {
setUpUnlockActionCard

pendingLoginRequests
if !store.state.isManageDevicesEnabled {
pendingLoginRequests
}
Comment thread
andrebispo5 marked this conversation as resolved.

if store.state.showUnlockOptions {
unlockOptionsSection
Expand Down Expand Up @@ -99,6 +101,15 @@ struct AccountSecurityView: View {
.imageStyle(.rowIcon)
}

if store.state.isManageDevicesEnabled {
SettingsListItem(
Localizations.devices,
accessibilityIdentifier: "ManageDevicesLabel",
) {
store.send(.manageDevicesTapped)
}
}

if store.state.isLockNowVisible {
SettingsListItem(
Localizations.lockNow,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import BitwardenKit

// MARK: - DeviceManagementAction

/// Actions that can be processed by a `DeviceManagementProcessor`.
///
enum DeviceManagementAction: Equatable {
/// A device was tapped.
case deviceTapped(DeviceListItem)

/// Dismiss the sheet.
case dismiss

/// The toast was shown or hidden.
case toastShown(Toast?)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// MARK: - DeviceManagementEffect

/// Effects that can be processed by a `DeviceManagementProcessor`.
///
enum DeviceManagementEffect: Equatable {
/// Load the device data.
case loadData
}
Loading
Loading