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
@@ -23,7 +23,21 @@ public extension TextFieldStyle where Self == ElementTextFieldStyle {
footerText: String? = nil, footerText: String? = nil,
isError: Bool = false, isError: Bool = false,
accessibilityIdentifier: String? = nil) -> ElementTextFieldStyle { accessibilityIdentifier: String? = nil) -> ElementTextFieldStyle {
ElementTextFieldStyle(labelText: labelText, footerText: footerText, isError: isError, accessibilityIdentifier: accessibilityIdentifier) ElementTextFieldStyle(labelText: labelText.map(Text.init),
footerText: footerText.map(Text.init),
isError: isError,
accessibilityIdentifier: accessibilityIdentifier)
}
@_disfavoredOverload
static func elementInput(labelText: Text? = nil,
footerText: Text? = nil,
isError: Bool = false,
accessibilityIdentifier: String? = nil) -> ElementTextFieldStyle {
ElementTextFieldStyle(labelText: labelText,
footerText: footerText,
isError: isError,
accessibilityIdentifier: accessibilityIdentifier)
} }
} }
@@ -36,8 +50,8 @@ public struct ElementTextFieldStyle: TextFieldStyle {
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
public let labelText: String? public let labelText: Text?
public let footerText: String? public let footerText: Text?
public let isError: Bool public let isError: Bool
public let accessibilityIdentifier: String? public let accessibilityIdentifier: String?
@@ -93,7 +107,7 @@ public struct ElementTextFieldStyle: TextFieldStyle {
/// - labelText: The text shown in the label above the field. /// - labelText: The text shown in the label above the field.
/// - footerText: The text shown in the footer label below the field. /// - footerText: The text shown in the footer label below the field.
/// - isError: Whether or not the text field is currently in the error state. /// - isError: Whether or not the text field is currently in the error state.
public init(labelText: String? = nil, footerText: String? = nil, isError: Bool = false, accessibilityIdentifier: String? = nil) { public init(labelText: Text? = nil, footerText: Text? = nil, isError: Bool = false, accessibilityIdentifier: String? = nil) {
self.labelText = labelText self.labelText = labelText
self.footerText = footerText self.footerText = footerText
self.isError = isError self.isError = isError
@@ -104,12 +118,10 @@ public struct ElementTextFieldStyle: TextFieldStyle {
let rectangle = RoundedRectangle(cornerRadius: 14.0) let rectangle = RoundedRectangle(cornerRadius: 14.0)
return VStack(alignment: .leading, spacing: 8) { return VStack(alignment: .leading, spacing: 8) {
if let labelText { labelText
Text(labelText) .font(.element.footnote)
.font(.element.footnote) .foregroundColor(labelColor)
.foregroundColor(labelColor) .padding(.horizontal, 16)
.padding(.horizontal, 16)
}
configuration configuration
.focused($isFocused) .focused($isFocused)
@@ -134,12 +146,11 @@ public struct ElementTextFieldStyle: TextFieldStyle {
textField.accessibilityIdentifier = accessibilityIdentifier textField.accessibilityIdentifier = accessibilityIdentifier
} }
if let footerText { footerText
Text(footerText) .tint(.element.links)
.font(.element.caption1) .font(.element.caption1)
.foregroundColor(footerColor) .foregroundColor(footerColor)
.padding(.horizontal, 16) .padding(.horizontal, 16)
}
} }
} }
} }
@@ -53,7 +53,7 @@
"session_verification_banner_message" = "Looks like you’re using a new device. Verify it’s you to access your encrypted messages."; "session_verification_banner_message" = "Looks like you’re using a new device. Verify it’s you to access your encrypted messages.";
"session_verification_start" = "Start"; "session_verification_start" = "Start";
"server_selection_server_footer" = "You can only connect to an existing server"; "server_selection_server_footer" = "You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %@";
"server_selection_sliding_sync_alert_title" = "Server not supported"; "server_selection_sliding_sync_alert_title" = "Server not supported";
"server_selection_sliding_sync_alert_message" = "This server currently doesn’t support sliding sync."; "server_selection_sliding_sync_alert_message" = "This server currently doesn’t support sliding sync.";
@@ -136,8 +136,10 @@ extension ElementL10n {
public static let screenshotDetectedMessage = ElementL10n.tr("Untranslated", "screenshot_detected_message") public static let screenshotDetectedMessage = ElementL10n.tr("Untranslated", "screenshot_detected_message")
/// You took a screenshot /// You took a screenshot
public static let screenshotDetectedTitle = ElementL10n.tr("Untranslated", "screenshot_detected_title") public static let screenshotDetectedTitle = ElementL10n.tr("Untranslated", "screenshot_detected_title")
/// You can only connect to an existing server /// You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %@
public static let serverSelectionServerFooter = ElementL10n.tr("Untranslated", "server_selection_server_footer") public static func serverSelectionServerFooter(_ p1: Any) -> String {
return ElementL10n.tr("Untranslated", "server_selection_server_footer", String(describing: p1))
}
/// This server currently doesn’t support sliding sync. /// This server currently doesn’t support sliding sync.
public static let serverSelectionSlidingSyncAlertMessage = ElementL10n.tr("Untranslated", "server_selection_sliding_sync_alert_message") public static let serverSelectionSlidingSyncAlertMessage = ElementL10n.tr("Untranslated", "server_selection_sliding_sync_alert_message")
/// Server not supported /// Server not supported
@@ -34,4 +34,21 @@ extension AttributedString {
return AttributedStringBuilderComponent(attributedString: attributedString, isBlockquote: isBlockquote, isReply: isReply) return AttributedStringBuilderComponent(attributedString: attributedString, isBlockquote: isBlockquote, isReply: isReply)
} }
} }
/// Replaces the specified placeholder with the a string that links to the specified URL.
/// - Parameters:
/// - linkPlaceholder: The text in the string that will be replaced. Make sure this is unique within the string.
/// - string: The text for the link that will be substituted into the placeholder.
/// - url: The URL that the link should open.
mutating func replace(_ linkPlaceholder: String, with string: String, asLinkTo url: URL) {
guard let range = range(of: linkPlaceholder) else {
MXLog.failure("Failed to find the link placeholder to be replaced.")
return
}
// Replace the placeholder with a link.
var replacement = AttributedString(string)
replacement.link = url
replaceSubrange(range, with: replacement)
}
} }
@@ -45,19 +45,9 @@ struct AnalyticsPromptStrings {
// Create the opt in content with a placeholder. // Create the opt in content with a placeholder.
let linkPlaceholder = "{link}" let linkPlaceholder = "{link}"
var optInContent = AttributedString(ElementL10n.analyticsOptInContent(InfoPlistReader.main.bundleDisplayName, linkPlaceholder)) var optInContent = AttributedString(ElementL10n.analyticsOptInContent(InfoPlistReader.main.bundleDisplayName, linkPlaceholder))
optInContent.replace(linkPlaceholder,
guard let range = optInContent.range(of: linkPlaceholder) else { with: ElementL10n.analyticsOptInContentLink,
self.optInContent = AttributedString(ElementL10n.analyticsOptInContent(InfoPlistReader.main.bundleDisplayName, asLinkTo: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
ElementL10n.analyticsOptInContentLink))
MXLog.failure("Failed to add a link attribute to the opt in content.")
return
}
// Replace the placeholder with a link.
var link = AttributedString(ElementL10n.analyticsOptInContentLink)
link.link = ServiceLocator.shared.settings.analyticsConfiguration.termsURL
optInContent.replaceSubrange(range, with: link)
self.optInContent = optInContent self.optInContent = optInContent
} }
} }
@@ -18,7 +18,10 @@ import SwiftUI
/// An image that is styled for use as the screen icon in the onboarding flow. /// An image that is styled for use as the screen icon in the onboarding flow.
struct AuthenticationIconImage: View { struct AuthenticationIconImage: View {
/// The icon that is shown.
let image: Image let image: Image
/// The amount of padding between the icon and the borders. Defaults to 16.
var insets: CGFloat = 16
var body: some View { var body: some View {
image image
@@ -27,9 +30,12 @@ struct AuthenticationIconImage: View {
.foregroundColor(.element.secondaryContent) .foregroundColor(.element.secondaryContent)
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.accessibilityHidden(true) .accessibilityHidden(true)
.padding(16) .padding(insets)
.frame(width: 72, height: 72) .frame(width: 70, height: 70)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.element.quinaryContent)) .background {
RoundedRectangle(cornerRadius: 14)
.fill(Color.element.quinaryContent)
}
} }
} }
@@ -37,6 +43,9 @@ struct AuthenticationIconImage: View {
struct AuthenticationIconImage_Previews: PreviewProvider { struct AuthenticationIconImage_Previews: PreviewProvider {
static var previews: some View { 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 { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
header header
.padding(.top, UIConstants.topPaddingToNavigationBar) .padding(.top, UIConstants.titleTopPaddingToNavigationBar)
.padding(.bottom, 32) .padding(.bottom, 32)
serverInfo serverInfo
@@ -70,15 +70,15 @@ struct LoginScreen: View {
var loginForm: some View { var loginForm: some View {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
Text(ElementL10n.ftueAuthSignInEnterDetails) Text(ElementL10n.ftueAuthSignInEnterDetails)
.font(.element.subheadline) .font(.element.footnote)
.foregroundColor(.element.primaryContent) .foregroundColor(.element.primaryContent)
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.bottom, 8) .padding(.bottom, 8)
TextField(ElementL10n.loginSigninUsernameHint, TextField(ElementL10n.username,
text: $context.username, text: $context.username,
// Prompt colour fixes a flicker that occurs before the text field style introspects the field. // 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) .focused($isUsernameFocused)
.textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername)) .textFieldStyle(.elementInput(accessibilityIdentifier: A11yIdentifiers.loginScreen.emailUsername))
.disableAutocorrection(true) .disableAutocorrection(true)
@@ -162,11 +162,18 @@ struct Login_Previews: PreviewProvider {
} }
static func screen(for viewModel: LoginViewModel) -> some View { static func screen(for viewModel: LoginViewModel) -> some View {
NavigationView { NavigationStack {
LoginScreen(context: viewModel.context) LoginScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.tint(.element.accent) .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 { var body: some View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text(ElementL10n.ftueAuthSignInChooseServerHeader) Text(ElementL10n.ftueAuthSignInChooseServerHeader)
.font(.element.subheadline) .font(.element.footnote)
.foregroundColor(.element.primaryContent) .foregroundColor(.element.primaryContent)
.padding(.horizontal, 16) .padding(.horizontal, 16)
@@ -24,6 +24,16 @@ enum ServerSelectionViewModelAction {
} }
struct ServerSelectionViewState: BindableState { 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. /// View state that can be bound to from SwiftUI.
var bindings: ServerSelectionBindings var bindings: ServerSelectionBindings
/// An error message to be shown in the text field footer. /// An error message to be shown in the text field footer.
@@ -32,8 +42,8 @@ struct ServerSelectionViewState: BindableState {
var isModallyPresented: Bool var isModallyPresented: Bool
/// The message to show in the text field footer. /// The message to show in the text field footer.
var footerMessage: String { var footerMessage: AttributedString {
footerErrorMessage ?? ElementL10n.serverSelectionServerFooter footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage
} }
/// The title shown on the confirm button. /// The title shown on the confirm button.
@@ -23,7 +23,7 @@ struct ServerSelectionScreen: View {
ScrollView { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
header header
.padding(.top, UIConstants.topPaddingToNavigationBar) .padding(.top, UIConstants.iconTopPaddingToNavigationBar)
.padding(.bottom, 36) .padding(.bottom, 36)
serverForm serverForm
@@ -40,7 +40,7 @@ struct ServerSelectionScreen: View {
/// The title, message and icon at the top of the screen. /// The title, message and icon at the top of the screen.
var header: some View { var header: some View {
VStack(spacing: 8) { VStack(spacing: 8) {
AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon)) AuthenticationIconImage(image: Image(asset: Asset.Images.serverSelectionIcon), insets: 19)
.padding(.bottom, 8) .padding(.bottom, 8)
Text(ElementL10n.ftueAuthChooseServerTitle) Text(ElementL10n.ftueAuthChooseServerTitle)
@@ -48,8 +48,8 @@ struct ServerSelectionScreen: View {
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.foregroundColor(.element.primaryContent) .foregroundColor(.element.primaryContent)
Text(ElementL10n.ftueAuthChooseServerSubtitle) Text(ElementL10n.ftueAuthChooseServerSignInSubtitle)
.font(.element.body) .font(.element.subheadline)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.foregroundColor(.element.tertiaryContent) .foregroundColor(.element.tertiaryContent)
} }
@@ -60,8 +60,8 @@ struct ServerSelectionScreen: View {
var serverForm: some View { var serverForm: some View {
VStack(alignment: .leading, spacing: 24) { VStack(alignment: .leading, spacing: 24) {
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress) TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress)
.textFieldStyle(.elementInput(labelText: ElementL10n.hsUrl, .textFieldStyle(.elementInput(labelText: Text(ElementL10n.hsUrl),
footerText: context.viewState.footerMessage, footerText: Text(context.viewState.footerMessage),
isError: context.viewState.isShowingFooterError, isError: context.viewState.isShowingFooterError,
accessibilityIdentifier: A11yIdentifiers.changeServerScreen.server)) accessibilityIdentifier: A11yIdentifiers.changeServerScreen.server))
.keyboardType(.URL) .keyboardType(.URL)
@@ -104,11 +104,10 @@ struct ServerSelectionScreen: View {
struct ServerSelection_Previews: PreviewProvider { struct ServerSelection_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ForEach(MockServerSelectionScreenState.allCases, id: \.self) { state in ForEach(MockServerSelectionScreenState.allCases, id: \.self) { state in
NavigationView { NavigationStack {
ServerSelectionScreen(context: state.viewModel.context) ServerSelectionScreen(context: state.viewModel.context)
.tint(.element.accent) .tint(.element.accent)
} }
.navigationViewStyle(.stack)
} }
} }
} }
@@ -28,7 +28,7 @@ struct SoftLogoutScreen: View {
ScrollView { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
header header
.padding(.top, UIConstants.topPaddingToNavigationBar) .padding(.top, UIConstants.titleTopPaddingToNavigationBar)
.padding(.bottom, 36) .padding(.bottom, 36)
switch context.viewState.loginMode { switch context.viewState.loginMode {
@@ -179,11 +179,18 @@ struct SoftLogout_Previews: PreviewProvider {
} }
static func screen(for viewModel: SoftLogoutViewModel) -> some View { static func screen(for viewModel: SoftLogoutViewModel) -> some View {
NavigationView { NavigationStack {
SoftLogoutScreen(context: viewModel.context) SoftLogoutScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.tint(.element.accent) .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 { struct UIConstants {
static let maxContentHeight: CGFloat = 750 static let maxContentHeight: CGFloat = 750
/// The padding used between the top of the main content and the navigation bar. /// The padding used between the top of the main content's icon and the navigation bar.
static let topPaddingToNavigationBar: CGFloat = 16 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. /// The padding used between the footer and the bottom of the view.
static let actionButtonBottomPadding: CGFloat = 24 static let actionButtonBottomPadding: CGFloat = 24
@@ -237,7 +237,7 @@ struct HomeScreen_Previews: PreviewProvider {
let viewModel = HomeScreenViewModel(userSession: userSession, let viewModel = HomeScreenViewModel(userSession: userSession,
attributedStringBuilder: AttributedStringBuilder()) attributedStringBuilder: AttributedStringBuilder())
return NavigationView { return NavigationStack {
HomeScreen(context: viewModel.context) HomeScreen(context: viewModel.context)
} }
} }
@@ -123,12 +123,12 @@ struct RoomScreen: View {
// MARK: - Previews // MARK: - Previews
struct RoomScreen_Previews: PreviewProvider { struct RoomScreen_Previews: PreviewProvider {
static var previews: some View { static let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(), mediaProvider: MockMediaProvider(),
mediaProvider: MockMediaProvider(), roomName: "Preview room")
roomName: "Preview room")
NavigationView { static var previews: some View {
NavigationStack {
RoomScreen(context: viewModel.context) RoomScreen(context: viewModel.context)
} }
} }
@@ -23,23 +23,25 @@ struct TimelineItemDebugView: View {
var content: String var content: String
} }
@Environment(\.presentationMode) private var presentationMode @Environment(\.dismiss) private var dismiss
let info: DebugInfo let info: DebugInfo
var body: some View { var body: some View {
NavigationView { NavigationStack {
ScrollView { ScrollView {
Text(info.content) Text(info.content)
.padding() .padding()
.font(.element.footnote) .font(.element.footnote)
.foregroundColor(.element.primaryContent)
.frame(maxWidth: .infinity, alignment: .leading)
} }
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.navigationTitle(info.title) .navigationTitle(info.title)
.toolbar { .toolbar {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button(ElementL10n.actionCancel) { Button(ElementL10n.actionCancel) {
presentationMode.wrappedValue.dismiss() dismiss()
} }
} }
ToolbarItem(placement: .secondaryAction) { ToolbarItem(placement: .secondaryAction) {
@@ -49,5 +51,27 @@ struct TimelineItemDebugView: View {
} }
} }
} }
.tint(.element.accent)
}
}
struct TimelineItemDebugView_Previews: PreviewProvider {
static let smallContent = """
{
SomeItem(
event_id: "$1234546634535",
sender: "@user:server.com",
timestamp: 42354534534
content: Message(
Message {
…
}
)
)
}
"""
static var previews: some View {
TimelineItemDebugView(info: .init(title: "Timeline item", content: smallContent))
} }
} }
@@ -87,7 +87,7 @@ struct TimelineTableView_Previews: PreviewProvider {
roomName: "Preview room") roomName: "Preview room")
static var previews: some View { static var previews: some View {
NavigationView { NavigationStack {
RoomScreen(context: viewModel.context) RoomScreen(context: viewModel.context)
} }
} }
@@ -20,7 +20,7 @@ struct SessionVerificationScreen: View {
@ObservedObject var context: SessionVerificationViewModel.Context @ObservedObject var context: SessionVerificationViewModel.Context
var body: some View { var body: some View {
NavigationView { NavigationStack {
ScrollView { ScrollView {
VStack(spacing: 32) { VStack(spacing: 32) {
screenHeader screenHeader
@@ -36,7 +36,6 @@ struct SessionVerificationScreen: View {
.background(Color.element.background.ignoresSafeArea()) .background(Color.element.background.ignoresSafeArea())
.safeAreaInset(edge: .bottom) { actionButtons.padding() } .safeAreaInset(edge: .bottom) { actionButtons.padding() }
} }
.navigationViewStyle(.stack)
.interactiveDismissDisabled() // Make sure dismissal goes through the state machine(s). .interactiveDismissDisabled() // Make sure dismissal goes through the state machine(s).
} }
@@ -183,12 +183,14 @@ private extension TimelineStyle {
// MARK: - Previews // MARK: - Previews
struct SettingsScreen_Previews: PreviewProvider { struct SettingsScreen_Previews: PreviewProvider {
static var previews: some View { static let viewModel = {
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"), let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
mediaProvider: MockMediaProvider()) mediaProvider: MockMediaProvider())
let viewModel = SettingsScreenViewModel(withUserSession: userSession) return SettingsScreenViewModel(withUserSession: userSession)
}()
NavigationView { static var previews: some View {
NavigationStack {
SettingsScreen(context: viewModel.context) SettingsScreen(context: viewModel.context)
.tint(.element.accent) .tint(.element.accent)
} }
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a41aec90e72bf87733ba3c5affb128531f02809d54d3034572fa095a8f0ab721 oid sha256:b4bc4e25efcb9e07898fdb6486727902aa2d7f2bb7df8e163a6082a6136a1817
size 91192 size 89873
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3ea593b5c20747ebc9a972689a4db0a131d2908b7a0d0794646606f6cf518ece oid sha256:c2bb6922c20a35da7aef9e756c5c3951c9e01a3dfa407f58ed69959b511f9537
size 76656 size 75911
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d44f42e63c990b2f012800494af6780fd24acea71e44ca3c361e407d9c7c311f oid sha256:6c3b9ffcf57deb42b4cb78fc2ea339afb88b66c2fbc543b97ba7d2a548b3f98c
size 83985 size 83159
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:51a2c9dc7b2049c0a351268187aad2854f15d1e5c10e9caf733d629a93b5a089 oid sha256:2b31aef830a765e8fc0eee3bc853b21c7c17afc760630f8325db42478d4161ca
size 90771 size 89503
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:830a542a2e546e84f00acd5e24ec1aca7e73aa602aac36868805e5c9b117620d oid sha256:08ccbb291e92a77b0494489f32fd71a1a7c3f93c5b3eaa0d3adbd0ee46ad463f
size 96188 size 98987
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:63f4e2039ed15cadf9d6af1aa9c4eb529d36586981d233d4f2b23e2a1ff1a8f1 oid sha256:0210f0729a0c724349146a0cbbc654016dbbb80789b2193bcec28e4b689637a2
size 167979 size 171245
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ad0710aa2ee9f311209fdeec522226fbf37012ef56a379a868c1228ffb67a70d oid sha256:cc36539eae4418ffc6be0a1c79700a8a04a0a2b526ab287abd84ac8d579849ea
size 97680 size 90984
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ba556cb20bc7c7426c5096f1c9698faa25c9cfc90c3911904ec679256fb23258 oid sha256:f10f8d4d47e9774ff5a536499b28a965f1691d176e3e9dc908657a7a6dae86b5
size 92481 size 95449
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:83c05698a6bbe4ecee5f05188b522695b496ff3c76606b31ccce0837f8c048e1 oid sha256:9816c9839b1c53e16a06fe9a17b362e82a4d7c6677b40aa61f4354626363ebae
size 80089 size 80103
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b2f133463e9be7e05cb2d5e53a8cbcba33d524b57321a89a3fc24bd16f9e09b8 oid sha256:f17d7a325c0e21254b69e75ea34d92e0db13bd1cb3fb398843f68f6a3f13fd35
size 81684 size 81693
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ea86739057e302f4c60805a0d986ac3fc04971de631abc8e640f7341844d1da0 oid sha256:f36618d1a5c044771b470c6e9dc69623ff6be43c8d52378540e5707a9aa96839
size 82459 size 82363
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5d249b34cbc9ad5de008c88d2fff88da9cf3cb9d84f4ad364c1bd70122f9b4d6 oid sha256:e4fc350e39d457f8a308af521af3427d72c383d274441b154e10b0a5259fc296
size 61347 size 61324
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9a2b81bed6c747c999852d5d52a665d915116bb23603a74415ce0c983528ce90 oid sha256:10b6d9c5ded6d4ccf5254253f8b40fd4a85874c0f97d6001db8f93a1b67d6202
size 145143 size 145036
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:502897556cd1f0ff24a4469845248e7a558d71312a8aea3ff0e24753b4470289 oid sha256:9ac5fe406934d77c87808a7f7168494a3276086b4add800cbd88e588ecbe57b0
size 142119 size 142115
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c00992310c100a4fc0c3793434ff35ebbb35ef954914c99e949bf744643e9aad oid sha256:f2e944e139033083033e477a32ffe3af852457572d96f9afd126225f4d548d2b
size 85707 size 85636
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1a8b3a881647e8f310c55236aee57460e6520fae9fa5f4c382baba46b49668a4 oid sha256:d851c65117003c330db9ea6a2a711525da3d8b6639dc5d0c1d3d1ddf02cbdd3a
size 82599 size 82280
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ecaeddba259d1a13f5e5b80d9e59a88302c33f5a57aead8a5a4a92cb93fa455d oid sha256:aa034d1cbdefdb5ecaaf90fb990d5c5c6d6575105f7ddc3ed5ccda1677f641c0
size 102062 size 100182
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:913989ca0c6f27c2cd1701c987ba5633d9eb9448863b38066f9b535ffa1cce7d oid sha256:0715d96372684171ef6fc61f17c76bf47acc65ea73bcb9f7b03d7b02406e8cd9
size 80014 size 79141
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:30ed8b2d0e9bdc5caddd9ecadfdbbf735970a7ae2c6d816072e18b690142804f oid sha256:e7ff29912bddf0a7cf58651df00ecde92b9841b3e182b38136adcaa228a886c8
size 90584 size 89539
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:109e28a53aab25f260b1f2946ab0a0e680efacad60055eb2720d4be5ab534055 oid sha256:01cb6b0bbce4a995ccb5e81ccc3a54f9d37448002e443bd819363bf72c2bee9e
size 100053 size 99707
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9da6d1ec94b9cba6b468b9e2368d3e9aa10b52599cfbf8bd18565e3c2f63cac2 oid sha256:e18912f41e73595bfc56e831b3baf67441a6b31fc2d2b361e3569afa44bc3f49
size 110691 size 114487
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8ab14c3f0a94c2fdef06fe1a8e8b60c0359d8e09587a1e70d86d171d6699bc89 oid sha256:1f4342e3845bf0ab2664f19bcd9bbf98c9f954109f829ce7f6ee81aa2c1dae89
size 172116 size 175337
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:dfbae5d3750202fa07e0c5c3a0a0e6841c77b90fed31c7d82aee4bd22e540703 oid sha256:e7d7a678b69e9d3cde1f19b6ec8b609a936ea321e53cbb4a003ee7f6c0bb600c
size 113499 size 102218
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:24fd364e71c2642fe88599642f906a07ba2101883993e8ca6b1cb114190e885e oid sha256:f1e5513207b68b17378df23a229cbb1cbeb3e1bdf7adea31b74827bf4aea42ec
size 106464 size 109589
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:aeb1181cc7660a73321cd70853eb4f7a1d656a1ecd6d1782611d60c936ba1708 oid sha256:4672d6427dcbed403779c3b4baa494f2c5d2830ad95bfa2396e3d959a04d9e7c
size 90315 size 89995
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ebbaad951950fdcc127e79cb23d5149d475b9ef31354db467adab86a552f721c oid sha256:7844dcd302f0c67a0ed8e9d7c9c50c5577d0c71b88598fa42998bae19a5ca867
size 90795 size 90253
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e530f21dc849e95ce38f475c1ae9ebde0a20fc3b62da17b7f32ee3592d4511ca oid sha256:a20998014db07e003cde17e499960b73543367935802b6b5fc51c55e164092dd
size 93196 size 92635
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:aa62c36a25016194059d1b08845ef498db059d4e1dd1c2e48e7cc02f79b13d95 oid sha256:53bda96a8dca3dbb5e3c48c522b24079ba59d93472435c05ba3b36034ce3f75e
size 60025 size 59492
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:efd982ac2d47a2bdc8fa2b25849e566130cfb35c95331340150ba4f9e9380ac2 oid sha256:b700ca16ad78b58d35a23bfc5889f71cb98e49a459b1f3f089a97ee1b1dee983
size 206687 size 206042
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ae440c97229af9d65d2b8147d22960fdaa0f8580e7193d44117362ba6df00d68 oid sha256:dce3111b9474295191ed34648fcdefc0709f6c583357a34b0f2ad742ca1d3416
size 203303 size 202738
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ebb9a93600c84cff88344a894577319bc0447b13b290f6ade3219686b5c131b8 oid sha256:cf9da344d757c985aa111516e5f7b6b3819fdd3c8d6e4ad70f2cab5bcf908642
size 94029 size 93697
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:eb500af458e236f912e2cf39a68126d91b91eb2ab6f4ac5f0317ac5bad1f116e oid sha256:b52bbe15a48467633513593065e68fcee225e33f7630b105a406d331d56fc3b5
size 93999 size 93451
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ba7a7a1cc7c1006a4a859dc532535bee0217a9624f10899265e9391df632ccf0 oid sha256:a4683098a91caed1dc2040833dabed1db830cec0757663bf0f98eec877c9a781
size 90724 size 89630
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:da60b298f838c5484052fca786da8dbcffcb2a5c9d989623f697e1d136cd79c2 oid sha256:36cd714098d67868223cd9532aba434ed0b26e093f49cb5e1728511da18baa1a
size 76264 size 75713
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8be57bcd3b0ea533dcc832494e3d066085b057f33b42b05e44c9386c448291ec oid sha256:077aefef1ac71ba09f80abab6480e0772c4ba97f8b53cf523d0395c622f66c17
size 80427 size 79823
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7cda6e325b169f50786cfb8444e27ff7213eefc6e5e81fd59cbb1ff9bf19a1bf oid sha256:354a89e44e590eaead83e7067b23c25bec856d24a96e7aa0edf47aa4ed6f9ddc
size 87464 size 86403
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5d9bdc17ccb874390a09167e2f895471282ffb9c46124a5fa765dfa0392b21f3 oid sha256:704a782a3fb3e9d99eafe567d305edf62f72109ce3442da7b4ab91f8cdb00b8f
size 91773 size 97501
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:87194387a1bcfbf41fe627cdbf4fde40a7b13e86fedbf7a2775565ec7824284a oid sha256:7f24087c1338f3a7cd3db0a91e67ab1fb333dbe3947addfca43f7224b0452658
size 163166 size 168863
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:02faff41eddb9ff07d305e8d84782509ce5e20fa0d557b0ff873ffd004e9e698 oid sha256:33e8ddb9558297a1cc3bbba5beb6c2e8803be372ac07c8788458b1b44625a647
size 92119 size 88331
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6c44709d238c582d2ed796b454816668257eb000548a0d3705a8eec7a5d75b49 oid sha256:7e3f25b2f80d9e58af3bc8986ac0b0d5dee8c6be99525a8fcc8221ea1b233906
size 88250 size 94136
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6e25078efecdd49161704a0b4d4d79a2c29be2e03ece5f506fd8905a433a52fe oid sha256:f60bdd21c520b57e532538b92528f5a6251a414d4ce699a316820954f8961f00
size 79746 size 79588
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ec3e7f457260f129e92f4b808c26b4f84c986708d3e7ee90b2fb7030ed21b446 oid sha256:bae46355106c0305085f65d27f23333d80b245cc8088f284bbd3b51f8ffc3e62
size 81570 size 81412
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ca5bc87197f0f0e3b67afea980efcc1e0b897281b3ee239e643cf7b3bbe581d7 oid sha256:511c408c1e191eb083a123759096fe150eaf0c55076e85ea2ae053ea16842baf
size 78767 size 78644
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1f92b2fd91e7e053f5f4d81369249a557bc7b3d2ea67784d5fabfde55e3eb9c1 oid sha256:623425d6c6fc2550e2c64bb30e0f574decc9b82e8a2f3aeb3aeeabd626183242
size 60368 size 60184
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0cd5f59758d322f69cc9ef4765f3e6d0176789c5796974daada81701e93801d0 oid sha256:e156ca20e05f1144eefaf08b23f093ef047591ceea283b5e3e8e02ac03e5e2e4
size 143094 size 142949
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:07070a48bf9ca9f91aa05eb166ddba6be038994573eb56d3572e3b0273199b0e oid sha256:8306d3378f076db67855a5d67e9a2e0c9209ab715449d2fd0ebee6459d896af1
size 140279 size 140084
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d6a95a58f6929124ec0924bdf9403eb7359cc91e52782a9e0d0d9e2f3b40118b oid sha256:ab68f33b50c4faa478bb3cdd70d1548aa2ab9c93895136a4ef752111fc759b0f
size 78759 size 78476
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9f8687aacb6c014bcaf7b89805f8ec59aba5b639f6cea43145ca3f77fc0ae76b oid sha256:687734b6fbf4e7a7fe29ebb66eb9b830211527dd2bb79dddcc8ca16c6f713414
size 79872 size 79798
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:86e3f7027c0823faecfacb6ed72f0334820bc77864c1e5c4edde89bf395f45eb oid sha256:a91f01196882f11f591c96bcdcb19ff0fcd96b30f395fdbf04c8e2fcf88e9a31
size 100916 size 98927
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0ccb72d29ddf6530de3934ef8f146a8744f5e8e42c10b3ce6d4d37fb10d83deb oid sha256:eb9e6ef890631bafdac2d88ee2c03a498685195e872b4b4f346373acf643f831
size 78882 size 77888
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:039827a112a2301031f0238e0c3db26e9875dcc62dea41de13ae555d4d4c60d5 oid sha256:f24909167e1807e2c4af0ee72a818c5f62a5327865337244c5d9e93cd5e16986
size 87717 size 86514
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:473129dc618b8edbb55686d00d3398a7ff2eb49874e9aa30e67ab0bc379906f1 oid sha256:a5ca48609e56626ddcf34b4ab49051861466024ab898e8a466c2e84e87b95476
size 95801 size 95251
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0e2de5b8b7f7620c6a0e1ccf8212b6f1442753de61682798ed17b9eca5f7ab14 oid sha256:91782800a413c273a3d3b5fd84896a5bde9a87ac25f199883d682e94006c3c85
size 108065 size 111953
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:79a5d7adbad04c4906ca4c585767df1d57f4ba1c1808f89ac244fd7a720cdcb7 oid sha256:b876c5df16b9421ac33cc2b937f752483414db06ddb6a2a25f6c0515cdf1b3f4
size 167896 size 173195
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:54181361dd0131adfd6033b4e0f11e81ce55314e97b375444f21fc14afb7c282 oid sha256:90467c2aecd998223d4aa4667a147bdd5329a90c09234cd895d7792b7bbd3080
size 107181 size 97824
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:20d4244743238cc8947708f046427d04bb3ec82560f732b50022e918c1c68172 oid sha256:5158f5470ea255e7780eae7b78c00c0e8737d3ba6894c2a805b77c3f002adb61
size 102884 size 107373
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:52581620b58fe494c04f86d8d33a327d72ba3f9cdaef09143b1ad81f0360a7c7 oid sha256:f964fd039bf67d735cd7b874e8d07e77bddf0df6753028843955965243d6b19f
size 89717 size 90676
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8060a5d47f7dec25e8267cae3c4febc49ec52d4d610fd50de85ef01889346838 oid sha256:b7745d9ffe4498cc50692b5d5ba48e46cf1af1c4ca40e4884e866fc0fa927bf7
size 90401 size 89509
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:aa16813fb58bc7689047e70eb1e880744216ca1c1c5efaf742644873dbd194fe oid sha256:0178a24ca8f960f9143d9163b0b7562f49fc43eaf973e1b555ad303be070b870
size 86320 size 85749
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2fdb235de9c09e524cd640041a0ab9538b1e7375a3db965efd7f46469853fb5e oid sha256:de89f64a203a03872435994a77b1e37ae20043e944b7ace6aa7a10f079ec4620
size 59942 size 58854
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:bce60d053368e96d679c164b55752aa867974402a2a7cefb08cb167e21018b61 oid sha256:bba553182b76ca2d8c8e87a422283f7c86d8d18e65fd3276743dc326200f6cff
size 203458 size 202848
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:556536f8c80e0f2daf0c48974c3d721255459c25e67e3ef5e71e40ea481979ed oid sha256:967ab4f50a2b2817f203f37d79c69612d094425b261b9ba3d1798d7201bf36b5
size 200273 size 199428
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:08712a31c8f7a48cfdf694c06a5a5b3298d07568fb7c4090e95f4c94fd014c3f oid sha256:b7070da776cc34235f02e8e4785be573d73ff04b89e9f784e13636eca06fa0ab
size 88332 size 88120
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:71c061ad002ddae2090688f85de989f933cf5d1df14a27d6f4968f1fb6a7c84d oid sha256:788adf3d340a02d7619cf541a025c7705d201b956128d9550e98ad977bc0463e
size 89870 size 89314
@@ -35,7 +35,8 @@ class ServerSelectionViewModelTests: XCTestCase {
func testErrorMessage() async throws { func testErrorMessage() async throws {
// Given a new instance of the view model. // Given a new instance of the view model.
XCTAssertNil(context.viewState.footerErrorMessage, "There should not be an error message for a new view model.") XCTAssertNil(context.viewState.footerErrorMessage, "There should not be an error message for a new view model.")
XCTAssertEqual(context.viewState.footerMessage, ElementL10n.serverSelectionServerFooter, "The standard footer message should be shown.") XCTAssertEqual(String(context.viewState.footerMessage.characters), ElementL10n.serverSelectionServerFooter(ElementL10n.actionLearnMore),
"The standard footer message should be shown.")
// When an error occurs. // When an error occurs.
let message = "Unable to contact server." let message = "Unable to contact server."
@@ -43,7 +44,7 @@ class ServerSelectionViewModelTests: XCTestCase {
// Then the footer should now be showing an error. // Then the footer should now be showing an error.
XCTAssertEqual(context.viewState.footerErrorMessage, message, "The error message should be stored.") XCTAssertEqual(context.viewState.footerErrorMessage, message, "The error message should be stored.")
XCTAssertEqual(context.viewState.footerMessage, message, "The error message should be shown.") XCTAssertEqual(String(context.viewState.footerMessage.characters), message, "The error message should be shown.")
// And when clearing the error. // And when clearing the error.
context.send(viewAction: .clearFooterError) context.send(viewAction: .clearFooterError)
@@ -53,6 +54,7 @@ class ServerSelectionViewModelTests: XCTestCase {
// Then the error message should now be removed. // Then the error message should now be removed.
XCTAssertNil(context.viewState.footerErrorMessage, "The error message should have been cleared.") XCTAssertNil(context.viewState.footerErrorMessage, "The error message should have been cleared.")
XCTAssertEqual(context.viewState.footerMessage, ElementL10n.serverSelectionServerFooter, "The standard footer message should be shown again.") XCTAssertEqual(String(context.viewState.footerMessage.characters), ElementL10n.serverSelectionServerFooter(ElementL10n.actionLearnMore),
"The standard footer message should be shown again.")
} }
} }
+1
View File
@@ -0,0 +1 @@
Update top padding and a string in Login and Server Selection screens.