Skip to content

Commit

Permalink
feat: create authentication identity input view - WPB-15221 (#2343)
Browse files Browse the repository at this point in the history
Co-authored-by: KaterinaWire <[email protected]>
  • Loading branch information
El-Fitz and KaterinaWire authored Jan 27, 2025
1 parent f5cd12e commit 0b9b184
Show file tree
Hide file tree
Showing 38 changed files with 399 additions and 59 deletions.
8 changes: 6 additions & 2 deletions WireUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ let package = Package(

.target(
name: "WireAuthenticationUI",
dependencies: ["WireDesign", "WireFoundation", "WireReusableUIComponents"]
dependencies: ["WireDesign", "WireFoundation", "WireReusableUIComponents"],
plugins: [.plugin(name: "SwiftGenPlugin", package: "WirePlugins")]
),
.testTarget(
name: "WireAuthenticationUITests",
dependencies: ["WireAuthenticationUI"]
),
.testTarget(name: "WireAuthenticationUITests", dependencies: ["WireAuthenticationUI"]),

.target(name: "WireConversationListUI"),
.testTarget(name: "WireConversationListUITests", dependencies: ["WireConversationListUI", "WireSettingsUI"]),
Expand Down
14 changes: 14 additions & 0 deletions WireUI/Sources/WireAuthenticationUI/.swiftgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Every input/output paths in the rest of the config will then be expressed relative to these.

input_dir: ./
output_dir: ${GENERATED}/

# Generate constants for your localized strings.

strings:
inputs:
- Resources/en.lproj/Localizable.strings
filter:
outputs:
- templateName: structured-swift5
output: Strings+Generated.swift
2 changes: 1 addition & 1 deletion WireUI/Sources/WireAuthenticationUI/Components/Logo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Logo: View {
Image(ImageResource(name: "logo", bundle: .module))
.resizable()
.scaledToFit()
.padding(.all, min(geometry.size.height, geometry.size.width) / 2.97)
.padding(.all, min(geometry.size.height, geometry.size.width) / 3)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

"authentication.identity.input.body" = "Simply enter your email address to start!";
"authentication.identity.input.field.placeholder" = "Email or SSO code";
"authentication.identity.input.field.title" = "Email or SSO code";
"authentication.identity.input.submit" = "Next";
"authentication.identity.input.terms" = "By pressing on “Next”, you accept Wire’s [Terms and Conditions](%@)";

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import SwiftUI
import WireDesign
import WireFoundation
import WireReusableUIComponents

package struct AuthenticationIdentityInputView: View {

package enum Action {
case submit(identity: String)
}

@State private var identity: String = ""
private let actionCallback: @Sendable (Action) -> Void
private let termsURL: URL

package init(actionCallback: @escaping @Sendable (Action) -> Void, termsURL: URL) {
self.actionCallback = actionCallback
self.termsURL = termsURL
}

package var body: some View {
VStack(alignment: .center, spacing: 16) {
HStack {
Spacer()
.frame(maxWidth: .infinity)
Logo()
.frame(width: 164, height: 95)
Spacer()
.frame(maxWidth: .infinity)
}
Text(L10n.Authentication.Identity.Input.body)
.wireTextStyle(.body1)
.lineLimit(nil)
LabeledTextField(
isMandatory: false,
placeholder: L10n.Authentication.Identity.Input.Field.placeholder,
title: L10n.Authentication.Identity.Input.Field.title,
string: $identity
)
.lineLimit(nil)
Button(action: {
actionCallback(.submit(identity: identity))
}, label: {
Text(L10n.Authentication.Identity.Input.submit)
.lineLimit(nil)
})
.wireButtonStyle(.primary)
.disabled(identity.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
Text(AttributedString.markdown(from: L10n.Authentication.Identity.Input.terms(termsURL.absoluteString)))
.multilineTextAlignment(.center)
.wireTextStyle(.subline1)
.lineLimit(nil)
}
}
}

struct AuthenticationIdentityInputPreview: View {
var body: some View {
AuthenticationIdentityInputView(
actionCallback: { _ in },
termsURL: URL(string: "https://example.com")!
)
.environment(\.wireTextStyleMapping, WireTextStyleMapping())
.padding(32)
}
}

#Preview {
BackgroundView()
.overlay {
VStack(spacing: 0) {
Spacer()
.frame(maxHeight: .infinity)
if #available(iOS 16.4, *) {
ScrollView(.vertical) {
AuthenticationIdentityInputPreview()
}
.background()
.scrollBounceBehavior(.basedOnSize)
} else {
ScrollView(.vertical) {
AuthenticationIdentityInputPreview()
}
.background()
}
}
}
}
4 changes: 4 additions & 0 deletions WireUI/Sources/WireDesign/Colors/ColorTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public enum ColorTheme {
public static let secondaryText = UIColor(light: .gray70, dark: .gray60)

public static let requiredField = UIColor(light: .red500Light, dark: .red500Dark)

public static let labelTitle = UIColor(light: .gray80, dark: .gray50)
public static let onDisabled = UIColor(light: .gray80, dark: .gray50)
}

public enum Backgrounds {
Expand Down Expand Up @@ -145,6 +148,7 @@ public enum ColorTheme {
public enum Strokes {

public static let outline = UIColor(light: .gray40, dark: .gray90)
public static let disabledOutline = UIColor(light: .gray50, dark: .gray80)
public static let dividersOutlineVariant = UIColor(light: .gray20, dark: .gray100)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,29 @@ import WireDesign
import WireFoundation

public struct LabeledTextField: View {
@Environment(\.isEnabled) private var isEnabled

private let isMandatory: Bool
private let placeholder: String?
private let title: String?

@FocusState var isFocused: Bool
@Binding private var string: String

public init(isMandatory: Bool = false, placeholder: String?, title: String?, string: Binding<String>) {
public init(
isMandatory: Bool = false,
placeholder: String?,
title: String?,
string: Binding<String>
) {
self.isMandatory = isMandatory
self.placeholder = placeholder
self.title = title
self._string = string
}

public var body: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 2) {
if let title {
(
isMandatory ? (
Expand All @@ -45,12 +52,68 @@ public struct LabeledTextField: View {
.foregroundColor(ColorTheme.Base.requiredField.color)
) : Text(title)
)
.wireTextStyle(.h4)
.foregroundStyle(titleColor)
.wireTextStyle(.subline1)
}
TextField(placeholder ?? "", text: $string)
.textFieldStyle(.roundedBorder)
.wireTextStyle(.body1)
HStack(spacing: 0) {
TextField(placeholder ?? "", text: $string)
.wireTextStyle(.body1)
.focused($isFocused)
.foregroundStyle(labelColor)
.padding(.vertical, 12)
if !string.isEmpty, isEnabled {
Button(action: {
string = ""
}, label: {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(.black)
.frame(width: 16, height: 16)
.padding(19)
})
}
}
.padding(.leading, 16)
.background {
if #available(iOS 17.0, *) {
RoundedRectangle(cornerRadius: 12)
.fill(labelBackgroundColor)
.stroke(labelBorderColor, lineWidth: 1)
} else {
RoundedRectangle(cornerRadius: 12)
.stroke(labelBorderColor, lineWidth: 1)
.background(labelBackgroundColor)
.cornerRadius(12)
}
}
}
}

private var titleColor: Color {
if isEnabled, isFocused {
return ColorTheme.Base.onPrimaryVariant.color
}
if isEnabled {
return ColorTheme.Base.labelTitle.color
}
return ColorTheme.Base.labelTitle.color
}

private var labelColor: Color {
isEnabled ? .primaryText : ColorTheme.Base.onDisabled.color
}

private var labelBackgroundColor: Color {
isEnabled ? .clear : ColorTheme.Backgrounds.background.color
}

private var labelBorderColor: Color {
if isEnabled, isFocused {
return ColorTheme.Base.onPrimaryVariant.color
}
if isEnabled {
return ColorTheme.Strokes.outline.color
}
return ColorTheme.Strokes.outline.color
}
}

Expand All @@ -61,22 +124,34 @@ public struct LabeledTextField: View {
title: nil,
string: .constant("")
)
.padding()
LabeledTextField(
isMandatory: false,
placeholder: "Placeholder",
title: "Some Title",
string: .constant("")
)
.padding()
LabeledTextField(
isMandatory: true,
placeholder: "Placeholder",
title: "Some Title",
string: .constant("")
)
.padding()
LabeledTextField(
isMandatory: true,
placeholder: "Placeholder",
title: "Some Title",
string: .constant("Lorem ipsum dolor sit amet, consectetur [...]")
)
.padding()
LabeledTextField(
isMandatory: true,
placeholder: "Placeholder",
title: "Some Title",
string: .constant("Lorem ipsum sic amet [...]")
string: .constant("Lorem ipsum dolor sit amet, consectetur [...]")
)
.padding()
.disabled(true)
}
Loading

0 comments on commit 0b9b184

Please sign in to comment.