implemented the labs screen

This commit is contained in:
Mauro Romito
2025-10-03 18:19:03 +02:00
committed by Mauro
parent b110a7ba51
commit b58d4269f4
20 changed files with 257 additions and 12 deletions
@@ -0,0 +1,29 @@
//
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
// periphery:ignore:all - this is just a labs remove this comment once generating the final file
import Combine
import SwiftUI
struct LabsScreenCoordinatorParameters {
let appSettings: AppSettings
}
final class LabsScreenCoordinator: CoordinatorProtocol {
private let viewModel: LabsScreenViewModelProtocol
init(parameters: LabsScreenCoordinatorParameters) {
viewModel = LabsScreenViewModel(labsOptions: parameters.appSettings)
}
func start() { }
func toPresentable() -> AnyView {
AnyView(LabsScreen(context: viewModel.context))
}
}
@@ -0,0 +1,37 @@
//
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Foundation
enum LabsScreenViewAction { }
enum LabsScreenViewModelAction { }
struct LabsScreenViewState: BindableState {
var bindings: LabsScreenViewStateBindings
}
// periphery: ignore - subscripts are seen as false positive
@dynamicMemberLookup
struct LabsScreenViewStateBindings {
private let labsOptions: LabsOptionsProtocol
init(labsOptions: LabsOptionsProtocol) {
self.labsOptions = labsOptions
}
subscript<Setting>(dynamicMember keyPath: ReferenceWritableKeyPath<LabsOptionsProtocol, Setting>) -> Setting {
get { labsOptions[keyPath: keyPath] }
set { labsOptions[keyPath: keyPath] = newValue }
}
}
protocol LabsOptionsProtocol: AnyObject {
var threadsEnabled: Bool { get set }
}
extension AppSettings: LabsOptionsProtocol { }
@@ -0,0 +1,25 @@
//
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Combine
import SwiftUI
typealias LabsScreenViewModelType = StateStoreViewModelV2<LabsScreenViewState, LabsScreenViewAction>
class LabsScreenViewModel: LabsScreenViewModelType, LabsScreenViewModelProtocol {
private let actionsSubject: PassthroughSubject<LabsScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<LabsScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(labsOptions: LabsOptionsProtocol) {
let bindings = LabsScreenViewStateBindings(labsOptions: labsOptions)
let state = LabsScreenViewState(bindings: bindings)
super.init(initialViewState: state)
}
}
@@ -0,0 +1,14 @@
//
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Combine
@MainActor
protocol LabsScreenViewModelProtocol {
var actionsPublisher: AnyPublisher<LabsScreenViewModelAction, Never> { get }
var context: LabsScreenViewModelType.Context { get }
}
@@ -0,0 +1,66 @@
//
// Copyright 2022-2025 New Vector 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 LabsScreen: View {
@Bindable var context: LabsScreenViewModel.Context
var body: some View {
Form {
header
settingsSection
}
.compoundList()
.navigationTitle(L10n.screenLabsTitle)
.navigationBarTitleDisplayMode(.inline)
}
private var header: some View {
Section {
EmptyView()
} header: {
VStack(spacing: 16) {
BigIcon(icon: \.labs, style: .default)
VStack(spacing: 8) {
Text(L10n.screenLabsHeaderTitle)
.foregroundColor(.compound.textPrimary)
.font(.compound.headingMDBold)
.multilineTextAlignment(.center)
Text(L10n.screenLabsHeaderDescription)
.font(.compound.bodyMD)
.multilineTextAlignment(.center)
.foregroundColor(.compound.textSecondary)
}
}
.frame(maxWidth: .infinity)
}
}
private var settingsSection: some View {
Section {
ListRow(label: .default(title: L10n.screenLabsEnableThreads,
icon: \.threads),
kind: .toggle($context.threadsEnabled))
}
}
}
// MARK: - Previews
struct LabsScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = LabsScreenViewModel(labsOptions: ServiceLocator.shared.settings)
static var previews: some View {
NavigationStack {
LabsScreen(context: viewModel.context)
}
}
}