Files
libnovel/ios/LibNovel/LibNovel/Views/Auth/AuthView.swift
Admin 88a25bc33e
All checks were successful
CI / Scraper / Lint (pull_request) Successful in 16s
CI / Scraper / Test (pull_request) Successful in 20s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Build (pull_request) Successful in 21s
CI / UI / Docker Push (pull_request) Has been skipped
iOS CI / Build (push) Successful in 7m18s
iOS CI / Build (pull_request) Successful in 5m22s
iOS CI / Test (push) Successful in 10m55s
iOS CI / Test (pull_request) Successful in 9m32s
refactor(ios): cleanup pass — dead code removal and component consolidation
- Remove dead structs: ReadingProgress, HomeData, PBList (Models.swift)
- Remove unused APIClient members: fetchRaw, setSessionId, BrowseParams; simplify logout(); drop no-op CodingKeys from BrowseResponse
- Remove absolutePrevChapter/absoluteNextChapter computed props (replaced by prevChapter/nextChapter); replace URLSession cover prefetch with Kingfisher
- Drop scrollOffset state var and dead subtitle block in ChapterRow (BookDetailView)
- Remove never-set chaptersLoading published prop (BookDetailViewModel)
- Add unified ChipButton(.filled/.outlined) to CommonViews replacing three near-identical pill chip types
- Replace FilterChipView+SortChip in LibraryView and FilterChip in BrowseView with ChipButton
- Replace inline KFImage usage in HomeView with AsyncCoverImage
- Fix deprecated .navigationBarHidden(true) → .toolbar(.hidden, for: .navigationBar) in AuthView
2026-03-10 17:13:45 +05:00

124 lines
4.5 KiB
Swift

import SwiftUI
struct AuthView: View {
@EnvironmentObject var authStore: AuthStore
@State private var mode: Mode = .login
@State private var username: String = ""
@State private var password: String = ""
@State private var confirmPassword: String = ""
@FocusState private var focusedField: Field?
enum Mode { case login, register }
enum Field { case username, password, confirmPassword }
var body: some View {
NavigationStack {
VStack(spacing: 0) {
// Logo / header
VStack(spacing: 8) {
Image(systemName: "books.vertical.fill")
.font(.system(size: 56))
.foregroundStyle(.amber)
Text("LibNovel")
.font(.largeTitle.bold())
}
.padding(.top, 60)
.padding(.bottom, 40)
// Tab switcher
Picker("Mode", selection: $mode) {
Text("Sign In").tag(Mode.login)
Text("Create Account").tag(Mode.register)
}
.pickerStyle(.segmented)
.padding(.horizontal, 24)
.padding(.bottom, 32)
// Form
VStack(spacing: 16) {
TextField("Username", text: $username)
.textFieldStyle(.roundedBorder)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.focused($focusedField, equals: .username)
.submitLabel(.next)
.onSubmit { focusedField = .password }
SecureField("Password", text: $password)
.textFieldStyle(.roundedBorder)
.focused($focusedField, equals: .password)
.submitLabel(mode == .register ? .next : .go)
.onSubmit {
if mode == .register { focusedField = .confirmPassword }
else { submit() }
}
if mode == .register {
SecureField("Confirm Password", text: $confirmPassword)
.textFieldStyle(.roundedBorder)
.focused($focusedField, equals: .confirmPassword)
.submitLabel(.go)
.onSubmit { submit() }
.transition(.opacity.combined(with: .move(edge: .top)))
}
}
.padding(.horizontal, 24)
.animation(.easeInOut(duration: 0.2), value: mode)
if let error = authStore.error {
Text(error)
.font(.footnote)
.foregroundStyle(.red)
.multilineTextAlignment(.center)
.padding(.horizontal, 24)
.padding(.top, 8)
}
Button(action: submit) {
Group {
if authStore.isLoading {
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
} else {
Text(mode == .login ? "Sign In" : "Create Account")
.fontWeight(.semibold)
}
}
.frame(maxWidth: .infinity)
.frame(height: 50)
}
.buttonStyle(.borderedProminent)
.tint(.amber)
.padding(.horizontal, 24)
.padding(.top, 24)
.disabled(authStore.isLoading || !formIsValid)
Spacer()
}
.toolbar(.hidden, for: .navigationBar)
}
.onChange(of: mode) { _, _ in
authStore.error = nil
confirmPassword = ""
}
}
private var formIsValid: Bool {
let base = !username.isEmpty && password.count >= 4
if mode == .register { return base && password == confirmPassword }
return base
}
private func submit() {
focusedField = nil
Task {
if mode == .login {
await authStore.login(username: username, password: password)
} else {
await authStore.register(username: username, password: password)
}
}
}
}