mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:31:27 +00:00
Add a Link New Device screen (behind a feature flag). (#4811)
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct LinkNewDeviceScreen: View {
|
||||
@Bindable var context: LinkNewDeviceScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
FullscreenDialog(topPadding: 24) {
|
||||
mainContent
|
||||
} bottomContent: {
|
||||
buttons
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgSubtleSecondary)
|
||||
.navigationTitle(L10n.commonLinkNewDevice)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var mainContent: some View {
|
||||
switch context.viewState.mode {
|
||||
case .loading, .readyToLink:
|
||||
VStack(spacing: 16) {
|
||||
BigIcon(icon: \.computer, style: .default)
|
||||
|
||||
Text(L10n.screenLinkNewDeviceRootTitle)
|
||||
.font(.compound.headingMDBold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
case .notSupported:
|
||||
VStack(spacing: 16) {
|
||||
BigIcon(icon: \.errorSolid, style: .alertSolid)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenLinkNewDeviceErrorNotSupportedTitle)
|
||||
.font(.compound.headingMDBold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text(L10n.screenLinkNewDeviceErrorNotSupportedSubtitle)
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.bottom, 40)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var buttons: some View {
|
||||
switch context.viewState.mode {
|
||||
case .loading:
|
||||
Button { } label: {
|
||||
Label {
|
||||
Text(L10n.commonLoading)
|
||||
} icon: {
|
||||
ProgressView()
|
||||
.tint(.compound.iconOnSolidPrimary)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.disabled(true)
|
||||
case .readyToLink(let isGeneratingCode):
|
||||
VStack(spacing: 16) {
|
||||
Button { context.send(viewAction: .linkMobileDevice) } label: {
|
||||
Label {
|
||||
Text(isGeneratingCode ? L10n.screenLinkNewDeviceRootLoadingQrCode : L10n.screenLinkNewDeviceRootMobileDevice)
|
||||
} icon: {
|
||||
if isGeneratingCode {
|
||||
ProgressView()
|
||||
.tint(.compound.iconOnSolidPrimary)
|
||||
} else {
|
||||
CompoundIcon(\.mobile)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
|
||||
Button { context.send(viewAction: .linkDesktopComputer) } label: {
|
||||
Label(L10n.screenLinkNewDeviceRootDesktopComputer, icon: \.computer)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
}
|
||||
.disabled(isGeneratingCode)
|
||||
case .notSupported:
|
||||
Button(L10n.actionDismiss) {
|
||||
context.send(viewAction: .dismiss)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct LinkNewDeviceScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = makeViewModel(mode: .readyToLink(isGeneratingCode: false))
|
||||
static let generatingViewModel = makeViewModel(mode: .readyToLink(isGeneratingCode: true))
|
||||
static let loadingViewModel = makeViewModel(mode: .loading)
|
||||
static let unsupportedViewModel = makeViewModel(mode: .notSupported)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
LinkNewDeviceScreen(context: viewModel.context)
|
||||
}
|
||||
.previewDisplayName("Ready")
|
||||
.snapshotPreferences(expect: viewModel.context.observe(\.viewState.mode).map { $0 == .readyToLink(isGeneratingCode: false) })
|
||||
|
||||
NavigationStack {
|
||||
LinkNewDeviceScreen(context: generatingViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Generating")
|
||||
.snapshotPreferences(expect: generatingViewModel.context.observe(\.viewState.mode).map { $0 == .readyToLink(isGeneratingCode: true) })
|
||||
|
||||
NavigationStack {
|
||||
LinkNewDeviceScreen(context: loadingViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Loading")
|
||||
|
||||
NavigationStack {
|
||||
LinkNewDeviceScreen(context: unsupportedViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Unsupported")
|
||||
.snapshotPreferences(expect: unsupportedViewModel.context.observe(\.viewState.mode).map { $0 == .notSupported })
|
||||
}
|
||||
|
||||
static func makeViewModel(mode: LinkNewDeviceScreenViewState.Mode) -> LinkNewDeviceScreenViewModel {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
clientProxy.isLoginWithQRCodeSupportedClosure = {
|
||||
switch mode {
|
||||
case .loading:
|
||||
try? await Task.sleep(for: .seconds(20))
|
||||
return false
|
||||
case .readyToLink:
|
||||
return true
|
||||
case .notSupported:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let viewModel = LinkNewDeviceScreenViewModel(clientProxy: clientProxy)
|
||||
|
||||
Task {
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
if case .readyToLink(isGeneratingCode: true) = mode {
|
||||
viewModel.context.send(viewAction: .linkMobileDevice)
|
||||
}
|
||||
}
|
||||
|
||||
return viewModel
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user