Files
swiftGrammar/AIGrammar/View/WordsView.swift
2024-08-12 10:49:20 +08:00

217 lines
8.3 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// WordsView.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
import ToastUI
struct WordsView: View {
@EnvironmentObject var globalEnv: GlobalEnvironment //
@State private var searchText = ""
@State private var showingResults = false
@State private var res = WordDetails()
@State private var isLoading = false //
@State private var showingToast = false // toast
@State private var toastText = ""
var body: some View {
NavigationView {
ZStack {
//
Color.pink.opacity(0.2).edgesIgnoringSafeArea(.all)
VStack(spacing: 0) {
//
HStack {
TextField("word", text: $searchText, onCommit: fetchWordData)
.padding(.horizontal, 40)
.padding(.vertical, 10)
.background(Color(.systemGray6))
.cornerRadius(8)
.font(.headline)
.overlay(
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading).padding(.leading, 8)
)
Spacer()
Button("Cancel") {
self.searchText = ""
self.showingResults = false
self.hideKeyboard()
}
}
.background(Color(.systemPink).opacity(0.2))
.cornerRadius(5) //
.shadow(radius: 2) //
.padding(8)
// 使 Spacer
if !showingResults {
Spacer()
}
if showingResults {
//
HStack {
List {
Section(header: Text("Definitions").font(.headline)) {
ForEach(res.explanations, id: \.self) { definition in
Text(definition)
.font(.system(.subheadline))
.padding(.leading, 4)
}
}
Section(header: Text("Common Phrases").font(.headline)) {
ForEach(res.phrases, id: \.self) { phrase in
Text(phrase)
.font(.system(.subheadline))
.padding(.leading, 4)
}
}
Section(header: Text("Synonyms").font(.system(size: UIFont.systemFontSize * 1.2))) {
ForEach(res.synonyms, id: \.self) { synonym in
Text(synonym)
.font(.system(.subheadline))
.padding(.leading, 4)
}
}
}
.padding(8)
.cornerRadius(5) //
.frame(maxHeight: .infinity) // ErrorCard */
}
.listStyle(GroupedListStyle()) // 使
}
}
.toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal) {
HStack {
Image(systemName: "exclamationmark.bubble")
.foregroundColor(.yellow)
Text(toastText)
.foregroundColor(.black)
}
.padding()
.background(Color.white)
.cornerRadius(8)
.shadow(radius: 10)
}
//
if isLoading {
LoadingView()
}
}
.onTapGesture {
// TextField
self.hideKeyboard()
}
.navigationBarTitle("Words", displayMode: .inline)
}
}
private func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
//
func loadData() {
isLoading = true
}
func loadComplete(){
isLoading = false
}
private func fetchWordData() {
//
let trimmedText = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
//
if trimmedText.isEmpty {
showingToast = true
toastText = "Please enter some text."
return //
}
// 200
if trimmedText.count > globalEnvironment.MaxLenWords {
showingToast = true
toastText = "Input too long. Please re-enter the text."
return //
}
//
let checkRes = CommonFunc.shared.validateInputEng(input: trimmedText, isSingleWord: true)
if !checkRes.isValid {
showingToast = true
toastText = checkRes.message
return
}
/*
if trimmedText.range(of: "[^a-zA-Z]", options: .regularExpression) != nil {
showingToast = true
toastText = "Please enter valid characters."
return //
}
*/
loadData() //
NetworkManager.shared.fetchWordDetails(inputText: trimmedText) { result in
switch result {
case .success(let wordDetails):
DispatchQueue.main.async {
res = wordDetails
showingResults = true
}
logger.info("fetch words succ.", context: ["word":wordDetails.word, "explanations":wordDetails.explanations, "phrases":wordDetails.phrases, "synonyms":wordDetails.synonyms])
case .failure(let error):
switch error {
case .businessError(let ret, let message):
//
logger.error("Business error - Ret: \(ret), Message: \(message)")
DispatchQueue.main.async {
showingToast = true
switch ret{
case globalEnvironment.RetCodeFreeLimited:
toastText = globalEnvironment.FreeLimitedToast
case globalEnvironment.RetCodeDirtyInput:
toastText = globalEnvironment.DirtyInputErrToast
default:
toastText = globalEnvironment.OtherServerErrToast
}
}
case .other(let error):
logger.error("network error occurred: \(error.localizedDescription)")
DispatchQueue.main.async {
showingToast = true
toastText = globalEnvironment.NetWorkErrToast
}
}
}
loadComplete()
}
}
}
struct WordsView_Previews: PreviewProvider {
static var previews: some View {
WordsView()
}
}
#Preview {
WordsView()
}