update the text input to support multiple lines

pull/891/head
Ryan ZHAO 1 year ago
parent 1a799d5565
commit 0fc40edb71

@ -30,14 +30,15 @@ struct NewMessageScreen: View {
if tabIndex == 0 { if tabIndex == 0 {
EnterAccountIdScreen( EnterAccountIdScreen(
accountIdOrONS: $accountIdOrONS, accountIdOrONS: $accountIdOrONS,
error: $errorString error: $errorString,
continueAction: continueWithAccountIdOrONS
) )
} }
else { else {
ScanQRCodeScreen( ScanQRCodeScreen(
$accountIdOrONS, $accountIdOrONS,
error: $errorString, error: $errorString,
continueAction: continueWithAccountId continueAction: continueWithAccountIdFromQRCode
) )
} }
} }
@ -54,10 +55,14 @@ struct NewMessageScreen: View {
} }
} }
func continueWithAccountId(onError: (() -> ())?) { func continueWithAccountIdFromQRCode(onError: (() -> ())?) {
startNewPrivateChatIfPossible(with: accountIdOrONS, onError: onError) startNewPrivateChatIfPossible(with: accountIdOrONS, onError: onError)
} }
func continueWithAccountIdOrONS() {
startNewDMIfPossible(with: accountIdOrONS, onError: nil)
}
fileprivate func startNewDMIfPossible(with onsNameOrPublicKey: String, onError: (() -> ())?) { fileprivate func startNewDMIfPossible(with onsNameOrPublicKey: String, onError: (() -> ())?) {
let maybeSessionId: SessionId? = SessionId(from: onsNameOrPublicKey) let maybeSessionId: SessionId? = SessionId(from: onsNameOrPublicKey)
@ -67,10 +72,10 @@ struct NewMessageScreen: View {
startNewDM(with: onsNameOrPublicKey) startNewDM(with: onsNameOrPublicKey)
case .blinded15, .blinded25: case .blinded15, .blinded25:
errorString = "DM_ERROR_DIRECT_BLINDED_ID".localized() errorString = "new_message_screen_error_msg_invalid_account_id".localized()
default: default:
errorString = "DM_ERROR_INVALID".localized() break
} }
return return
} }
@ -93,6 +98,8 @@ struct NewMessageScreen: View {
switch error { switch error {
case .decryptionFailed, .hashingFailed, .validationFailed: case .decryptionFailed, .hashingFailed, .validationFailed:
messageOrNil = error.errorDescription messageOrNil = error.errorDescription
case .generic:
messageOrNil = "new_message_screen_error_msg_network_issue".localized()
default: break default: break
} }
} }
@ -102,8 +109,8 @@ struct NewMessageScreen: View {
} }
return (maybeSessionId?.prefix == .blinded15 || maybeSessionId?.prefix == .blinded25 ? return (maybeSessionId?.prefix == .blinded15 || maybeSessionId?.prefix == .blinded25 ?
"DM_ERROR_DIRECT_BLINDED_ID".localized() : "new_message_screen_error_msg_invalid_account_id".localized() :
"DM_ERROR_INVALID".localized() "new_message_screen_error_msg_unrecognized_ons".localized()
) )
}() }()
@ -133,6 +140,8 @@ struct NewMessageScreen: View {
struct EnterAccountIdScreen: View { struct EnterAccountIdScreen: View {
@Binding var accountIdOrONS: String @Binding var accountIdOrONS: String
@Binding var error: String? @Binding var error: String?
@State var isTextFieldInErrorMode: Bool = false
var continueAction: () -> ()
var body: some View { var body: some View {
VStack( VStack(
@ -143,9 +152,7 @@ struct EnterAccountIdScreen: View {
$accountIdOrONS, $accountIdOrONS,
placeholder: "new_message_screen_enter_account_id_hint".localized(), placeholder: "new_message_screen_enter_account_id_hint".localized(),
error: $error error: $error
) ) {
if error?.isEmpty != true {
ZStack { ZStack {
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
Text("\("new_message_screen_enter_account_id_explanation".localized())\(Image(systemName: "questionmark.circle"))") Text("\("new_message_screen_enter_account_id_explanation".localized())\(Image(systemName: "questionmark.circle"))")
@ -160,7 +167,7 @@ struct EnterAccountIdScreen: View {
} }
} }
.padding(.horizontal, Values.smallSpacing) .padding(.horizontal, Values.smallSpacing)
.padding(.top, -50) .padding(.top, Values.smallSpacing)
.onTapGesture { .onTapGesture {
if let url: URL = URL(string: "https://sessionapp.zendesk.com/hc/en-us/articles/4439132747033-How-do-Session-ID-usernames-work-") { if let url: URL = URL(string: "https://sessionapp.zendesk.com/hc/en-us/articles/4439132747033-How-do-Session-ID-usernames-work-") {
UIApplication.shared.open(url) UIApplication.shared.open(url)
@ -172,7 +179,7 @@ struct EnterAccountIdScreen: View {
if !accountIdOrONS.isEmpty { if !accountIdOrONS.isEmpty {
Button { Button {
continueAction()
} label: { } label: {
Text("next".localized()) Text("next".localized())
.bold() .bold()

@ -877,3 +877,6 @@ The point that a message will disappear in a disappearing message update message
"new_message_screen_enter_account_id_tab_title" = "Enter Account ID"; "new_message_screen_enter_account_id_tab_title" = "Enter Account ID";
"new_message_screen_enter_account_id_hint" = "Enter Account ID or ONS"; "new_message_screen_enter_account_id_hint" = "Enter Account ID or ONS";
"new_message_screen_enter_account_id_explanation" = "Start a new conversation by entering your friend's Account ID, ONS or scanning their QR code."; "new_message_screen_enter_account_id_explanation" = "Start a new conversation by entering your friend's Account ID, ONS or scanning their QR code.";
"new_message_screen_error_msg_invalid_account_id" = "This Account ID is invalid. Please check and try again.";
"new_message_screen_error_msg_unrecognized_ons" = "We couldnt recognize this ONS. Please check and try again.";
"new_message_screen_error_msg_network_issue" = "We were unable to search for this ONS. Please try again later.";

@ -53,7 +53,7 @@ struct DisplayNameScreen: View {
$displayName, $displayName,
placeholder: "onboarding_display_name_hint".localized(), placeholder: "onboarding_display_name_hint".localized(),
error: $error error: $error
) ) {}
Spacer(minLength: 0) Spacer(minLength: 0)
.frame(maxHeight: Values.massiveSpacing) .frame(maxHeight: Values.massiveSpacing)

@ -163,7 +163,7 @@ struct EnterRecoveryPasswordScreen: View{
$recoveryPassword, $recoveryPassword,
placeholder: "onboarding_recovery_password_hint".localized(), placeholder: "onboarding_recovery_password_hint".localized(),
error: $error error: $error
) ) {}
Spacer(minLength: 0) Spacer(minLength: 0)
.frame(maxHeight: Values.massiveSpacing) .frame(maxHeight: Values.massiveSpacing)

@ -2,12 +2,14 @@
import SwiftUI import SwiftUI
import Combine import Combine
import UIKit
public struct SessionTextField: View { public struct SessionTextField<ExplanationView>: View where ExplanationView: View {
@Binding var text: String @Binding var text: String
@Binding var error: String? @Binding var error: String?
@State var previousError: String = "" @State var previousError: String = ""
let explanationView: () -> ExplanationView
let placeholder: String let placeholder: String
var textThemeColor: ThemeValue { var textThemeColor: ThemeValue {
(error?.isEmpty == false) ? .danger : .textPrimary (error?.isEmpty == false) ? .danger : .textPrimary
@ -18,13 +20,15 @@ public struct SessionTextField: View {
return false return false
} }
static let height: CGFloat = isIPhone5OrSmaller ? CGFloat(48) : CGFloat(80) let height: CGFloat = isIPhone5OrSmaller ? CGFloat(48) : CGFloat(80)
static let cornerRadius: CGFloat = 13 let cornerRadius: CGFloat = 13
public init(_ text: Binding<String>, placeholder: String, error: Binding<String?>) { public init(_ text: Binding<String>, placeholder: String, error: Binding<String?>, @ViewBuilder explanationView: @escaping () -> ExplanationView) {
self._text = text self._text = text
self.placeholder = placeholder self.placeholder = placeholder
self._error = error self._error = error
self.explanationView = explanationView
UITextView.appearance().backgroundColor = .clear
} }
public var body: some View { public var body: some View {
@ -32,15 +36,29 @@ public struct SessionTextField: View {
alignment: .center, alignment: .center,
spacing: Values.smallSpacing spacing: Values.smallSpacing
) { ) {
ZStack(alignment: .topLeading) { ZStack(alignment: .leading) {
if text.isEmpty { if text.isEmpty {
Text(placeholder) Text(placeholder)
.font(.system(size: Values.smallFontSize)) .font(.system(size: Values.smallFontSize))
.foregroundColor(themeColor: isErrorMode ? .danger : .textSecondary) .foregroundColor(themeColor: isErrorMode ? .danger : .textSecondary)
} }
if #available(iOS 16.0, *) {
SwiftUI.TextField( SwiftUI.TextField(
"", "",
text: $text.onChange{ value in
if error?.isEmpty == false && text != value {
previousError = error!
error = nil
}
},
axis: .vertical
)
.font(.system(size: Values.smallFontSize))
.foregroundColor(themeColor: textThemeColor)
} else if #available(iOS 14.0, *) {
ZStack {
TextEditor(
text: $text.onChange{ value in text: $text.onChange{ value in
if error?.isEmpty == false && text != value { if error?.isEmpty == false && text != value {
previousError = error! previousError = error!
@ -50,20 +68,49 @@ public struct SessionTextField: View {
) )
.font(.system(size: Values.smallFontSize)) .font(.system(size: Values.smallFontSize))
.foregroundColor(themeColor: textThemeColor) .foregroundColor(themeColor: textThemeColor)
.transparentScrolling()
.frame(maxHeight: self.height)
.padding(.all, -4)
// FIXME: This is a workaround for dynamic height of the TextEditor.
Text(text.isEmpty ? placeholder : text)
.font(.system(size: Values.smallFontSize))
.opacity(0)
.padding(.all, 4)
.frame(
maxWidth: .infinity,
maxHeight: self.height
)
}
.fixedSize(horizontal: false, vertical: true)
} else {
SwiftUI.TextField(
"",
text: $text.onChange{ value in
if error?.isEmpty == false && text != value {
previousError = error!
error = nil
}
}
)
.font(.system(size: Values.smallFontSize))
.foregroundColor(themeColor: textThemeColor)
}
} }
.padding(.horizontal, Values.largeSpacing) .padding(.horizontal, Values.largeSpacing)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: Self.height) .frame(height: self.height)
.overlay( .overlay(
RoundedRectangle( RoundedRectangle(
cornerSize: CGSize( cornerSize: CGSize(
width: Self.cornerRadius, width: self.cornerRadius,
height: Self.cornerRadius height: self.cornerRadius
) )
) )
.stroke(themeColor: isErrorMode ? .danger : .borderSeparator) .stroke(themeColor: isErrorMode ? .danger : .borderSeparator)
) )
if isErrorMode {
ZStack { ZStack {
Text(error ?? previousError) Text(error ?? previousError)
.bold() .bold()
@ -75,6 +122,9 @@ public struct SessionTextField: View {
height: 50, height: 50,
alignment: .top alignment: .top
) )
} else {
explanationView()
}
} }
} }
} }
@ -82,7 +132,13 @@ public struct SessionTextField: View {
struct SessionTextField_Previews: PreviewProvider { struct SessionTextField_Previews: PreviewProvider {
@State static var text: String = "test" @State static var text: String = "test"
@State static var error: String? = "test error" @State static var error: String? = "test error"
@State static var emptyText: String = ""
@State static var emptyError: String? = nil
static var previews: some View { static var previews: some View {
SessionTextField($text, placeholder: "Placeholder", error: $error) VStack {
SessionTextField($text, placeholder: "Placeholder", error: $error) {}
SessionTextField($emptyText, placeholder: "Placeholder", error: $emptyError) {}
}
} }
} }

@ -102,6 +102,16 @@ extension View {
public func toastView(message: Binding<String?>) -> some View { public func toastView(message: Binding<String?>) -> some View {
self.modifier(ToastModifier(message: message)) self.modifier(ToastModifier(message: message))
} }
public func transparentScrolling() -> some View {
if #available(iOS 16.0, *) {
return scrollContentBackground(.hidden)
} else {
return onAppear {
UITextView.appearance().backgroundColor = .clear
}
}
}
} }
extension Binding { extension Binding {

Loading…
Cancel
Save