Consolidate onboarding into single login page with native styling and full localization

Merge the two-page welcome/login flow into a single page with the app icon,
title, subtitle, and login inputs all on one screen. Replace the custom blue
background and white-on-blue styling with native iOS system colors and
button styles. Add missing translations (de, es, fr) for all onboarding
strings and fix localization by using LocalizedStringKey and String(localized:).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 10:44:51 +01:00
parent ce2a814e5a
commit c8d9ab7397
4 changed files with 4178 additions and 3886 deletions

View File

@@ -15,56 +15,55 @@ struct TokenLoginView: View {
@Binding var showAlert: Bool
@Binding var alertMessage: String
@FocusState private var focusedField: Field?
@State var userSettings = UserSettings.shared
// TextField handling
enum Field {
case server
case username
case token
}
var body: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 16) {
ServerAddressField()
.padding(.bottom)
LoginLabel(text: "User name")
BorderedLoginTextField(example: "username", text: $userSettings.username)
.focused($focusedField, equals: .username)
.textContentType(.username)
.submitLabel(.next)
.padding(.bottom)
LoginLabel(text: "App Token")
BorderedLoginTextField(example: "can be generated in security settings of your nextcloud", text: $userSettings.token)
.focused($focusedField, equals: .token)
.textContentType(.password)
.submitLabel(.join)
HStack{
Spacer()
Button {
Task {
if await loginCheck(nextcloudLogin: false) {
userSettings.onboarding = false
}
}
} label: {
Text("Submit")
.foregroundColor(.white)
.font(.headline)
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.white, lineWidth: 2)
.foregroundColor(.clear)
)
}
.padding()
Spacer()
VStack(alignment: .leading, spacing: 6) {
LoginLabel(text: "User name")
BorderedLoginTextField(example: "username", text: $userSettings.username)
.focused($focusedField, equals: .username)
.textContentType(.username)
.submitLabel(.next)
}
VStack(alignment: .leading, spacing: 6) {
LoginLabel(text: "App Token")
BorderedLoginTextField(example: "can be generated in security settings of your nextcloud", text: $userSettings.token)
.focused($focusedField, equals: .token)
.textContentType(.password)
.submitLabel(.join)
}
Button {
Task {
if await loginCheck(nextcloudLogin: false) {
userSettings.onboarding = false
}
}
} label: {
Label("Submit", systemImage: "person.badge.key")
.font(.subheadline)
.fontWeight(.medium)
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.foregroundStyle(Color.nextcloudBlue)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(Color.nextcloudBlue.opacity(0.1))
)
}
.padding(.top, 4)
}
.onSubmit {
switch focusedField {
@@ -77,14 +76,14 @@ struct TokenLoginView: View {
}
}
}
func loginCheck(nextcloudLogin: Bool) async -> Bool {
if userSettings.serverAddress == "" {
alertMessage = "Please enter a server address!"
alertMessage = String(localized: "Please enter a server address!")
showAlert = true
return false
} else if !nextcloudLogin && (userSettings.username == "" || userSettings.token == "") {
alertMessage = "Please enter a user name and app token!"
alertMessage = String(localized: "Please enter a user name and app token!")
showAlert = true
return false
}
@@ -95,7 +94,7 @@ struct TokenLoginView: View {
let _ = try await client.getCategories()
return true
} catch {
alertMessage = "Login failed. Please check your inputs and internet connection."
alertMessage = String(localized: "Login failed. Please check your inputs and internet connection.")
showAlert = true
return false
}