Onboarding flow coordinator and FTUE changes (#2578)

Fixes #2595, fixes #2594, fixes #2593, fixes #2592, fixes #2591
This commit is contained in:
Stefan Ceriu
2024-03-21 14:01:23 +02:00
committed by GitHub
parent 9e6b6ba60a
commit a62c96f1c5
176 changed files with 1886 additions and 912 deletions
+9 -1
View File
@@ -34,10 +34,18 @@ extension ProcessInfo {
false
#endif
}
static var isRunningIntegrationTests: Bool {
#if DEBUG
processInfo.environment["IS_RUNNING_INTEGRATION_TESTS"] == "1"
#else
false
#endif
}
/// Flag indicating whether the app is running the UI tests or unit tests.
static var isRunningTests: Bool {
isRunningUITests || isRunningUnitTests
isRunningUITests || isRunningUnitTests || isRunningIntegrationTests
}
/// The identifier of the screen to be loaded when running UI tests.
@@ -33,6 +33,8 @@ struct FullscreenDialog<Content: View, BottomContent: View>: View {
/// The spacing between the content and the buttons.
var spacing: CGFloat = 16
var showBackgroundGradient = false
/// The main content shown at the top of the layout.
@ViewBuilder var content: () -> Content
/// The content shown at the bottom of the layout.
@@ -62,23 +64,32 @@ struct FullscreenDialog<Content: View, BottomContent: View>: View {
}
.scrollBounceBehavior(.basedOnSize)
.safeAreaInset(edge: .bottom) {
VStack(spacing: 0) {
bottomContent()
.readableFrame()
.padding(.horizontal, horizontalPadding)
.padding(.top, spacing)
.padding(.bottom, UIConstants.actionButtonBottomPadding)
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))
if showBackgroundGradient {
adjustedBottomContent(geometry: geometry)
.background(Asset.Images.backgroundBottom.swiftUIImage.resizable().ignoresSafeArea())
} else {
adjustedBottomContent(geometry: geometry)
.background()
}
.background()
}
}
}
private func adjustedBottomContent(geometry: GeometryProxy) -> some View {
VStack(spacing: 0) {
bottomContent()
.readableFrame()
.padding(.horizontal, horizontalPadding)
.padding(.top, spacing)
.padding(.bottom, UIConstants.actionButtonBottomPadding)
Spacer()
.frame(height: UIConstants.spacerHeight(in: geometry))
}
}
/// A continuously scrolling layout used for accessibility font sizes.
var accessibilityLayout: some View {
private var accessibilityLayout: some View {
GeometryReader { geometry in
ScrollView {
VStack(spacing: 0) {
@@ -21,35 +21,61 @@ import SwiftUI
/// takes a compound icon. If you would like to apply it to an SFSymbol, you can call
/// the `heroImage()` modifier directly on the Image.
struct HeroImage: View {
enum Style {
case normal
case positive
var foregroundColor: Color {
switch self {
case .normal:
return .compound.iconPrimary
case .positive:
return .compound.iconSuccessPrimary
}
}
var backgroundFillColor: Color {
switch self {
case .normal:
return .compound.bgSubtleSecondary
case .positive:
return .compound.bgSuccessSubtle
}
}
}
/// The icon that is shown.
let icon: KeyPath<CompoundIcons, Image>
var style: Style = .normal
var body: some View {
CompoundIcon(icon, size: .custom(42), relativeTo: .title)
.modifier(HeroImageModifier())
.modifier(HeroImageModifier(style: style))
}
}
extension Image {
/// Styles the image for use as the main/top/hero screen icon. You should prefer
/// the HeroImage component when possible, by using an icon from Compound.
func heroImage(insets: CGFloat = 16) -> some View {
func heroImage(insets: CGFloat = 16, style: HeroImage.Style = .normal) -> some View {
resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.scaledPadding(insets, relativeTo: .title)
.modifier(HeroImageModifier())
.modifier(HeroImageModifier(style: style))
}
}
private struct HeroImageModifier: ViewModifier {
let style: HeroImage.Style
func body(content: Content) -> some View {
content
.foregroundColor(.compound.iconPrimary)
.scaledFrame(size: 70, relativeTo: .title)
.foregroundColor(style.foregroundColor)
.background {
RoundedRectangle(cornerRadius: 14)
.fill(Color.compound.bgSubtleSecondary)
.fill(style.backgroundFillColor)
}
.accessibilityHidden(true)
}
@@ -0,0 +1,104 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// Represents the position of a checkmark item in a list.
enum ListPosition {
case top, middle, bottom
/// The corners that should be rounded for this position.
var roundedCorners: UIRectCorner {
switch self {
case .top:
return [.topLeft, .topRight]
case .middle:
return []
case .bottom:
return [.bottomLeft, .bottomRight]
}
}
}
struct RoundedLabelItem<Icon: View>: View {
@Environment(\.backgroundStyle) private var backgroundStyle
let title: String
let listPosition: ListPosition
let iconContent: () -> Icon
private var backgroundColor: AnyShapeStyle {
backgroundStyle ?? AnyShapeStyle(.compound.bgSubtleSecondary)
}
var body: some View {
Label { Text(title) } icon: {
iconContent()
}
.labelStyle(CheckmarkLabelStyle())
.padding(.horizontal, 20)
.padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.background(backgroundColor, in: RoundedCornerShape(radius: 16, corners: listPosition.roundedCorners))
}
}
private struct CheckmarkLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline, spacing: 16) {
configuration.icon
.font(.compound.bodyLGSemibold)
configuration.title
.font(.compound.bodyMDSemibold)
}
.foregroundColor(.compound.textPrimary)
}
}
// MARK: - Previews
struct AnalyticsPromptScreenCheckmarkItem_Previews: PreviewProvider, TestablePreview {
static let strings = AnalyticsPromptScreenStrings(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
@ViewBuilder
static var testImage1: some View {
Image(systemName: "circle")
}
@ViewBuilder
static var testImage2: some View {
Image(systemName: "square")
}
static var previews: some View {
VStack(alignment: .leading, spacing: 4) {
RoundedLabelItem(title: strings.point1, listPosition: .top) {
testImage1
}
RoundedLabelItem(title: strings.point2, listPosition: .middle) {
testImage2
}
RoundedLabelItem(title: "This is a short string.", listPosition: .middle) {
testImage1
}
RoundedLabelItem(title: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.",
listPosition: .bottom) {
testImage2
}
}
.padding()
}
}