Update login and server selection screens (#651)

* Update login and server selection screens.

* Replace all NavigationViews with NavigationStacks
This commit is contained in:
Doug
2023-03-02 18:05:18 +00:00
committed by GitHub
parent e7a8dc8cc2
commit 1c09a7eace
84 changed files with 287 additions and 205 deletions
@@ -18,7 +18,10 @@ import SwiftUI
/// An image that is styled for use as the screen icon in the onboarding flow.
struct AuthenticationIconImage: View {
/// The icon that is shown.
let image: Image
/// The amount of padding between the icon and the borders. Defaults to 16.
var insets: CGFloat = 16
var body: some View {
image
@@ -27,9 +30,12 @@ struct AuthenticationIconImage: View {
.foregroundColor(.element.secondaryContent)
.aspectRatio(contentMode: .fit)
.accessibilityHidden(true)
.padding(16)
.frame(width: 72, height: 72)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.element.quinaryContent))
.padding(insets)
.frame(width: 70, height: 70)
.background {
RoundedRectangle(cornerRadius: 14)
.fill(Color.element.quinaryContent)
}
}
}
@@ -37,6 +43,9 @@ struct AuthenticationIconImage: View {
struct AuthenticationIconImage_Previews: PreviewProvider {
static var previews: some View {
AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon))
HStack(spacing: 20) {
AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon), insets: 19)
AuthenticationIconImage(image: Image(systemName: "hourglass"))
}
}
}
@@ -28,7 +28,7 @@ struct LoginScreen: View {
ScrollView {
VStack(spacing: 0) {
header
.padding(.top, UIConstants.topPaddingToNavigationBar)
.padding(.top, UIConstants.titleTopPaddingToNavigationBar)
.padding(.bottom, 32)
serverInfo
@@ -70,15 +70,15 @@ struct LoginScreen: View {
var loginForm: some View {
VStack(alignment: .leading, spacing: 0) {
Text(ElementL10n.ftueAuthSignInEnterDetails)
.font(.element.subheadline)
.font(.element.footnote)
.foregroundColor(.element.primaryContent)
.padding(.horizontal, 16)
.padding(.bottom, 8)
TextField(ElementL10n.loginSigninUsernameHint,
TextField(ElementL10n.username,
text: $context.username,
// Prompt colour fixes a flicker that occurs before the text field style introspects the field.
prompt: Text(ElementL10n.loginSigninUsernameHint).foregroundColor(.element.tertiaryContent))
prompt: Text(ElementL10n.username).foregroundColor(.element.tertiaryContent))
.focused($isUsernameFocused)
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername))
.disableAutocorrection(true)
@@ -162,11 +162,18 @@ struct Login_Previews: PreviewProvider {
}
static func screen(for viewModel: LoginViewModel) -> some View {
NavigationView {
NavigationStack {
LoginScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline)
.tint(.element.accent)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button { } label: {
Text("\(Image(systemName: "chevron.backward")) Back")
}
}
}
}
.navigationViewStyle(.stack)
.tint(.element.accent)
}
}
@@ -31,7 +31,7 @@ struct LoginServerInfoSection: View {
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(ElementL10n.ftueAuthSignInChooseServerHeader)
.font(.element.subheadline)
.font(.element.footnote)
.foregroundColor(.element.primaryContent)
.padding(.horizontal, 16)
@@ -24,6 +24,16 @@ enum ServerSelectionViewModelAction {
}
struct ServerSelectionViewState: BindableState {
/// The message to be shown in the text field footer when no error has occurred.
private let regularFooterMessage = {
let linkPlaceholder = "{link}"
var message = AttributedString(ElementL10n.serverSelectionServerFooter(linkPlaceholder))
message.replace(linkPlaceholder,
with: ElementL10n.actionLearnMore,
asLinkTo: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
return message
}()
/// View state that can be bound to from SwiftUI.
var bindings: ServerSelectionBindings
/// An error message to be shown in the text field footer.
@@ -32,8 +42,8 @@ struct ServerSelectionViewState: BindableState {
var isModallyPresented: Bool
/// The message to show in the text field footer.
var footerMessage: String {
footerErrorMessage ?? ElementL10n.serverSelectionServerFooter
var footerMessage: AttributedString {
footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage
}
/// The title shown on the confirm button.
@@ -23,7 +23,7 @@ struct ServerSelectionScreen: View {
ScrollView {
VStack(spacing: 0) {
header
.padding(.top, UIConstants.topPaddingToNavigationBar)
.padding(.top, UIConstants.iconTopPaddingToNavigationBar)
.padding(.bottom, 36)
serverForm
@@ -40,7 +40,7 @@ struct ServerSelectionScreen: View {
/// The title, message and icon at the top of the screen.
var header: some View {
VStack(spacing: 8) {
AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon))
AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon), insets: 19)
.padding(.bottom, 8)
Text(ElementL10n.ftueAuthChooseServerTitle)
@@ -48,8 +48,8 @@ struct ServerSelectionScreen: View {
.multilineTextAlignment(.center)
.foregroundColor(.element.primaryContent)
Text(ElementL10n.ftueAuthChooseServerSubtitle)
.font(.element.body)
Text(ElementL10n.ftueAuthChooseServerSignInSubtitle)
.font(.element.subheadline)
.multilineTextAlignment(.center)
.foregroundColor(.element.tertiaryContent)
}
@@ -60,8 +60,8 @@ struct ServerSelectionScreen: View {
var serverForm: some View {
VStack(alignment: .leading, spacing: 24) {
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress)
.textFieldStyle(.elementInput(labelText: ElementL10n.hsUrl,
footerText: context.viewState.footerMessage,
.textFieldStyle(.elementInput(labelText: Text(ElementL10n.hsUrl),
footerText: Text(context.viewState.footerMessage),
isError: context.viewState.isShowingFooterError,
accessibilityIdentifier: A11yIdentifiers.changeServerScreen.server))
.keyboardType(.URL)
@@ -104,11 +104,10 @@ struct ServerSelectionScreen: View {
struct ServerSelection_Previews: PreviewProvider {
static var previews: some View {
ForEach(MockServerSelectionScreenState.allCases, id: \.self) { state in
NavigationView {
NavigationStack {
ServerSelectionScreen(context: state.viewModel.context)
.tint(.element.accent)
}
.navigationViewStyle(.stack)
}
}
}
@@ -28,7 +28,7 @@ struct SoftLogoutScreen: View {
ScrollView {
VStack(spacing: 0) {
header
.padding(.top, UIConstants.topPaddingToNavigationBar)
.padding(.top, UIConstants.titleTopPaddingToNavigationBar)
.padding(.bottom, 36)
switch context.viewState.loginMode {
@@ -179,11 +179,18 @@ struct SoftLogout_Previews: PreviewProvider {
}
static func screen(for viewModel: SoftLogoutViewModel) -> some View {
NavigationView {
NavigationStack {
SoftLogoutScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline)
.tint(.element.accent)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button { } label: {
Text("\(Image(systemName: "chevron.backward")) Back")
}
}
}
}
.navigationViewStyle(.stack)
.tint(.element.accent)
}
}
@@ -20,8 +20,10 @@ import SwiftUI
struct UIConstants {
static let maxContentHeight: CGFloat = 750
/// The padding used between the top of the main content and the navigation bar.
static let topPaddingToNavigationBar: CGFloat = 16
/// The padding used between the top of the main content's icon and the navigation bar.
static let iconTopPaddingToNavigationBar: CGFloat = 43
/// The padding used between the top of the main content's title and the navigation bar.
static let titleTopPaddingToNavigationBar: CGFloat = 32
/// The padding used between the footer and the bottom of the view.
static let actionButtonBottomPadding: CGFloat = 24