Skip to content

Commit

Permalink
Implement the home screen recovery key confirmation banner.
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanceriu authored and pixlwave committed Oct 27, 2023
1 parent 49b392d commit 2b10ebf
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 59 deletions.
8 changes: 8 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
5770C4906668C6D3008A2AC9 /* SessionVerificationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5046BB295AEAFA6FB81655 /* SessionVerificationScreenModels.swift */; };
5780E444F405AA1304E1C23E /* DeveloperOptionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E521D6C2BF8DF0DFB35146 /* DeveloperOptionsScreen.swift */; };
584590D0EA548152A393E72C /* HomeScreenSessionVerificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = E71C28CF29CD05B6D6AE8580 /* HomeScreenSessionVerificationBanner.swift */; };
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; };
5894C2514400A4FBC9327632 /* ServerConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03277E40D0E0DE0712021A71 /* ServerConfirmationScreenCoordinator.swift */; };
5897A59DDBD3592282092223 /* MediaSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */; };
Expand Down Expand Up @@ -667,6 +668,7 @@
AF8BFA37791E1756EE243E08 /* SettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */; };
AFA1F2543DFF7B45DF68ACD6 /* CompletionSuggestionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170BF6F7923A5C3792442F27 /* CompletionSuggestionModels.swift */; };
AFC518DCC38B821537EBF549 /* CreatePollScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADC55DFF46083BC957E0019 /* CreatePollScreenModels.swift */; };
B04E9EB589CE99C3929E817A /* HomeScreenRecoveryKeyConfirmationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */; };
B064D42BA087649ACAE462E8 /* SoftLogoutUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55F30E764BED111C81739844 /* SoftLogoutUITests.swift */; };
B09DC6E3D0EE87C4D4ABFAB3 /* EncryptedHistoryRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0140615D2232612C813FD6C /* EncryptedHistoryRoomTimelineItem.swift */; };
B0CB16349B96262AA65A04AF /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; };
Expand Down Expand Up @@ -1040,6 +1042,7 @@
04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = "<group>"; };
052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModel.swift; sourceTree = "<group>"; };
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = "<group>"; };
05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRecoveryKeyConfirmationBanner.swift; sourceTree = "<group>"; };
05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenCoordinator.swift; sourceTree = "<group>"; };
05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = "<group>"; };
0685156EB62D7E243F097CFC /* ServerSelectionScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelProtocol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1832,6 +1835,7 @@
E6E6BDF9D26DB05C88901416 /* RedactedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineItem.swift; sourceTree = "<group>"; };
E6F5D66F158A6662F953733E /* NotificationSettingsProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxy.swift; sourceTree = "<group>"; };
E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
E71C28CF29CD05B6D6AE8580 /* HomeScreenSessionVerificationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenSessionVerificationBanner.swift; sourceTree = "<group>"; };
E80F9E9B93B6ECE9A937B1C6 /* FormRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRow.swift; sourceTree = "<group>"; };
E8294DB9E95C0C0630418466 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorStateMachine.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2865,8 +2869,10 @@
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */,
C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */,
24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */,
05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */,
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */,
C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */,
E71C28CF29CD05B6D6AE8580 /* HomeScreenSessionVerificationBanner.swift */,
F3BC2D3573D900A9C9F8C191 /* HomeScreenUserMenuButton.swift */,
);
path = View;
Expand Down Expand Up @@ -5447,8 +5453,10 @@
77BB228AEA861E50FFD6A228 /* HomeScreenEmptyStateView.swift in Sources */,
64C373ACCFA26D42BA45CFAD /* HomeScreenInvitesButton.swift in Sources */,
8810A2A30A68252EBB54EE05 /* HomeScreenModels.swift in Sources */,
B04E9EB589CE99C3929E817A /* HomeScreenRecoveryKeyConfirmationBanner.swift in Sources */,
0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */,
A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */,
584590D0EA548152A393E72C /* HomeScreenSessionVerificationBanner.swift in Sources */,
D3986615892E7CF05C86518A /* HomeScreenUserMenuButton.swift in Sources */,
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */,
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@
"common_waiting_for_decryption_key" = "Waiting for decryption key";
"common_poll_end_confirmation" = "Are you sure you want to end this poll?";
"common_poll_summary" = "Poll: %1$@";
"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to confirm your recovery key to maintain access to your chat backup.";
"confirm_recovery_key_banner_title" = "Confirm your recovery key";
"crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?";
"dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings.";
"dialog_permission_generic" = "Please grant the permission in the system settings.";
Expand All @@ -196,7 +198,6 @@
"error_failed_loading_messages" = "Failed loading messages";
"error_failed_locating_user" = "%1$@ could not access your location. Please try again later.";
"error_failed_uploading_voice_message" = "Failed to upload your voice message.";
"error_missing_location_auth_ios" = "%1$@ does not have permission to access your location. You can enable access in Settings > Location";
"error_no_compatible_app_found" = "No compatible app was found to handle this action.";
"error_some_messages_have_not_been_sent" = "Some messages have not been sent";
"error_unknown" = "Sorry, an error occurred";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {

// The navigation stack doesn't like it if the root and the push happen
// on the same loop run
DispatchQueue.main.async {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
self.presentSecureBackupScreen(animated: animated)
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.processEvent(.feedbackScreen)
case .presentSessionVerificationScreen:
stateMachine.processEvent(.showSessionVerificationScreen)
case .presentSecureBackupSettings:
settingsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
case .presentStartChatScreen:
stateMachine.processEvent(.showStartChatScreen)
case .logout:
Expand Down
8 changes: 4 additions & 4 deletions ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ public enum L10n {
public static var commonWaiting: String { return L10n.tr("Localizable", "common_waiting") }
/// Waiting for decryption key
public static var commonWaitingForDecryptionKey: String { return L10n.tr("Localizable", "common_waiting_for_decryption_key") }
/// Your chat backup is currently out of sync. You need to confirm your recovery key to maintain access to your chat backup.
public static var confirmRecoveryKeyBannerMessage: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_message") }
/// Confirm your recovery key
public static var confirmRecoveryKeyBannerTitle: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_title") }
/// %1$@ crashed the last time it was used. Would you like to share a crash report with us?
public static func crashDetectionDialogContent(_ p1: Any) -> String {
return L10n.tr("Localizable", "crash_detection_dialog_content", String(describing: p1))
Expand Down Expand Up @@ -442,10 +446,6 @@ public enum L10n {
}
/// Failed to upload your voice message.
public static var errorFailedUploadingVoiceMessage: String { return L10n.tr("Localizable", "error_failed_uploading_voice_message") }
/// %1$@ does not have permission to access your location. You can enable access in Settings > Location
public static func errorMissingLocationAuthIos(_ p1: Any) -> String {
return L10n.tr("Localizable", "error_missing_location_auth_ios", String(describing: p1))
}
/// No compatible app was found to handle this action.
public static var errorNoCompatibleAppFound: String { return L10n.tr("Localizable", "error_no_compatible_app_found") }
/// Some messages have not been sent
Expand Down
1 change: 1 addition & 0 deletions ElementX/Sources/Other/AccessibilityIdentifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct A11yIdentifiers {
let settings = "home_screen-settings"
let signOut = "home_screen-sign_out"
let verificationBannerContinue = "home_screen-verification_continue"
let recoveryKeyConfirmationBannerContinue = "home_screen-recovery_key_confirmation_continue"
let invites = "home_screen-invites"
let startChat = "home_screen-start_chat"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum HomeScreenCoordinatorAction {
case presentSettingsScreen
case presentFeedbackScreen
case presentSessionVerificationScreen
case presentSecureBackupSettings
case presentStartChatScreen
case presentInvitesScreen
case logout
Expand Down Expand Up @@ -75,6 +76,8 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.presentSettingsScreen)
case .presentSessionVerificationScreen:
actionsSubject.send(.presentSessionVerificationScreen)
case .presentSecureBackupSettings:
actionsSubject.send(.presentSecureBackupSettings)
case .logout:
actionsSubject.send(.logout)
case .presentStartChatScreen:
Expand Down
6 changes: 5 additions & 1 deletion ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum HomeScreenViewModelAction {
case presentRoomDetails(roomIdentifier: String)
case roomLeft(roomIdentifier: String)
case presentSessionVerificationScreen
case presentSecureBackupSettings
case presentSettingsScreen
case presentFeedbackScreen
case presentStartChatScreen
Expand All @@ -44,7 +45,9 @@ enum HomeScreenViewAction {
case userMenu(action: HomeScreenViewUserMenuAction)
case startChat
case verifySession
case confirmRecoveryKey
case skipSessionVerification
case skipRecoveryKeyConfirmation
case updateVisibleItemRange(range: Range<Int>, isScrolling: Bool)
case selectInvites
}
Expand All @@ -70,7 +73,8 @@ struct HomeScreenViewState: BindableState {
let userID: String
var userDisplayName: String?
var userAvatarURL: URL?
var showSessionVerificationBanner = false
var needsSessionVerification = false
var needsRecoveryKeyConfirmation = false
var showUserMenuBadge = false
var showSettingsMenuOptionBadge = false
var rooms: [HomeScreenRoom] = []
Expand Down
25 changes: 15 additions & 10 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.sink { [weak self] callback in
switch callback {
case .sessionVerificationNeeded:
self?.state.showSessionVerificationBanner = true
self?.state.needsSessionVerification = true
case .didVerifySession:
self?.state.showSessionVerificationBanner = false
self?.state.needsSessionVerification = false
default:
break
}
Expand All @@ -82,14 +82,15 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol

userSession.clientProxy.secureBackupController.recoveryKeyState
.receive(on: DispatchQueue.main)
.map { state in
state == .unknown || state == .disabled
}
.sink { [weak self] requiresSecureBackupSetup in
guard let self else { return }
.sink { [weak self] recoveryKeyState in
guard let self, appSettings.chatBackupEnabled else { return }

let requiresSecureBackupSetup = recoveryKeyState == .unknown || recoveryKeyState == .disabled || recoveryKeyState == .incomplete

state.showUserMenuBadge = requiresSecureBackupSetup
state.showSettingsMenuOptionBadge = requiresSecureBackupSetup

state.showUserMenuBadge = requiresSecureBackupSetup && appSettings.chatBackupEnabled
state.showSettingsMenuOptionBadge = requiresSecureBackupSetup && appSettings.chatBackupEnabled
state.needsRecoveryKeyConfirmation = recoveryKeyState == .incomplete
}
.store(in: &cancellables)

Expand Down Expand Up @@ -143,8 +144,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}
case .verifySession:
actionsSubject.send(.presentSessionVerificationScreen)
case .confirmRecoveryKey:
actionsSubject.send(.presentSecureBackupSettings)
case .skipSessionVerification:
state.showSessionVerificationBanner = false
state.needsSessionVerification = false
case .skipRecoveryKeyConfirmation:
state.needsRecoveryKeyConfirmation = false
case .updateVisibleItemRange(let range, let isScrolling):
visibleItemRangePublisher.send((range, isScrolling))
case .startChat:
Expand Down
55 changes: 13 additions & 42 deletions ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@ struct HomeScreen: View {
@ViewBuilder
/// The session verification banner and invites button if either are needed.
private var topSection: some View {
if context.viewState.showSessionVerificationBanner {
sessionVerificationBanner
if context.viewState.needsSessionVerification {
HomeScreenSessionVerificationBanner(context: context)
} else if context.viewState.needsRecoveryKeyConfirmation {
HomeScreenRecoveryKeyConfirmationBanner(context: context)
}

if context.viewState.hasPendingInvitations, !context.isSearchFieldFocused {
Expand All @@ -204,43 +206,7 @@ struct HomeScreen: View {
.padding(.vertical, -8.0)
}
}

private var sessionVerificationBanner: some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading, spacing: 4) {
HStack(spacing: 16) {
Text(L10n.sessionVerificationBannerTitle)
.font(.compound.bodyLGSemibold)
.foregroundColor(.compound.textPrimary)

Spacer()

Button {
context.send(viewAction: .skipSessionVerification)
} label: {
Image(systemName: "xmark")
.foregroundColor(.compound.iconSecondary)
.frame(width: 12, height: 12)
}
}
Text(L10n.sessionVerificationBannerMessage)
.font(.compound.bodyMD)
.foregroundColor(.compound.textSecondary)
}

Button(L10n.actionContinue) {
context.send(viewAction: .verifySession)
}
.frame(maxWidth: .infinity)
.buttonStyle(.compound(.primary, size: .medium))
.accessibilityIdentifier(A11yIdentifiers.homeScreen.verificationBannerContinue)
}
.padding(16)
.background(Color.compound.bgSubtleSecondary)
.cornerRadius(14)
.padding(.horizontal, 16)
}


@ToolbarContentBuilder
private var toolbar: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {
Expand Down Expand Up @@ -333,13 +299,18 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
}

static func viewModel(_ state: MockRoomSummaryProviderState) -> HomeScreenViewModel {
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
roomSummaryProvider: MockRoomSummaryProvider(state: state)),
let clientProxy = MockClientProxy(userID: "@alice:example.com",
roomSummaryProvider: MockRoomSummaryProvider(state: state))

let userSession = MockUserSession(clientProxy: clientProxy,
mediaProvider: MockMediaProvider(),
voiceMessageMediaManager: VoiceMessageMediaManagerMock())

let attributedStringBuilder = AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
mentionBuilder: MentionBuilder())

return HomeScreenViewModel(userSession: userSession,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder()),
attributedStringBuilder: attributedStringBuilder,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
Expand Down
Loading

0 comments on commit 2b10ebf

Please sign in to comment.