Initial commit

This commit is contained in:
oscarz
2024-08-12 10:26:21 +08:00
parent 3637120e84
commit 3002510aaf
46 changed files with 1210 additions and 29 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.in-app-payments</key>
<array/>
</dict>
</plist>

View File

@ -0,0 +1,80 @@
//
// AllTab.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
@main
struct LearningToolApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
TabView {
GrammarCheckView()
.tabItem {
Image(systemName: "book.fill")
Text("Grammar Check")
}
WordsView()
.tabItem {
Image(systemName: "text.bubble")
Text("Words")
}
TranslateView()
.tabItem {
Image(systemName: "globe")
Text("Translate")
}
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
}
}
}
struct GrammarCheckView: View {
var body: some View {
// Your Grammar Check View content goes here.
Text("Grammar Check")
}
}
struct WordsView: View {
var body: some View {
// Your Words View content goes here.
Text("Words")
}
}
struct TranslateView: View {
var body: some View {
// Your Translate View content goes here.
Text("Translate")
}
}
struct SettingsView: View {
var body: some View {
// Your Settings View content goes here.
Text("Settings")
}
}
#Preview {
AllTab()
}

View File

@ -1,13 +1,120 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"images": [
{
"size": "20x20",
"idiom": "universal",
"filename": "icon-20@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "20x20",
"idiom": "universal",
"filename": "icon-20@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "29x29",
"idiom": "universal",
"filename": "icon-29@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "29x29",
"idiom": "universal",
"filename": "icon-29@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "40x40",
"idiom": "universal",
"filename": "icon-40@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "40x40",
"idiom": "universal",
"filename": "icon-40@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "60x60",
"idiom": "universal",
"filename": "icon-60@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "60x60",
"idiom": "universal",
"filename": "icon-60@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "64x64",
"idiom": "universal",
"filename": "icon-64@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "64x64",
"idiom": "universal",
"filename": "icon-64@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "68x68",
"idiom": "universal",
"filename": "icon-68@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "76x76",
"idiom": "universal",
"filename": "icon-76@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "83.5x83.5",
"idiom": "universal",
"filename": "icon-83.5@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "1024x1024",
"idiom": "universal",
"filename": "icon-1024.png",
"scale": "1x",
"platform": "ios"
}
],
"info": {
"version": 1,
"author": "icon.wuruihong.com"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,33 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"filename" : "crown.png",
"idiom" : "iphone",
"scale" : "1x"
},
{
"idiom" : "iphone",
"scale" : "2x"
},
{
"idiom" : "iphone",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,18 @@
//
// LoadingView.swift
// AIGrammar
//
// Created by oscar on 2024/7/7.
//
import SwiftUI
struct LoadingView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
LoadingView()
}

View File

@ -0,0 +1,18 @@
//
// CameraView.swift
// AIGrammar
//
// Created by oscar on 2024/4/3.
//
import SwiftUI
struct CameraView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
CameraView()
}

View File

@ -0,0 +1,18 @@
//
// IAPTestView.swift
// AIGrammar
//
// Created by oscar on 2024/6/19.
//
import SwiftUI
struct IAPTestView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
IAPTestView()
}

View File

@ -0,0 +1,18 @@
//
// InputView.swift
// AIGrammar
//
// Created by oscar on 2024/4/1.
//
import SwiftUI
struct InputView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
InputView()
}

View File

@ -0,0 +1,18 @@
//
// ResultView.swift
// AIGrammar
//
// Created by oscar on 2024/4/1.
//
import SwiftUI
struct ResultView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
ResultView()
}

View File

@ -0,0 +1,18 @@
//
// RichText.swift
// AIGrammar
//
// Created by oscar on 2024/4/3.
//
import SwiftUI
struct RichText: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
RichText()
}

View File

@ -0,0 +1,18 @@
//
// ShareSheet.swift
// AIGrammar
//
// Created by oscar on 2024/4/3.
//
import SwiftUI
struct ShareSheet: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
ShareSheet()
}

View File

@ -0,0 +1,18 @@
//
// GrammarCheckView.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
struct GrammarCheckView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
GrammarCheckView()
}

View File

@ -0,0 +1,18 @@
//
// IAPView.swift
// AIGrammar
//
// Created by oscar on 2024/7/9.
//
import SwiftUI
struct IAPView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
IAPView()
}

View File

@ -0,0 +1,18 @@
//
// SettingsView.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
struct SettingsView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
SettingsView()
}

View File

@ -0,0 +1,18 @@
//
// TranslateView.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
struct TranslateView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
TranslateView()
}

View File

@ -0,0 +1,18 @@
//
// WordsView.swift
// AIGrammar
//
// Created by oscar on 2024/3/27.
//
import SwiftUI
struct WordsView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
WordsView()
}

View File

@ -0,0 +1,8 @@
//
// config.swift
// AIGrammar
//
// Created by oscar on 2024/3/28.
//
import Foundation

View File

@ -0,0 +1,22 @@
//
// DemoData.swift
// AIGrammar
//
// Created by oscar on 2024/4/2.
//
import Foundation
struct GrammarRes {
var tPlain : String
var tType : String
var tReason : String
var tCorrection : [String]
}
struct GrammarData {
var tInputText : String
var tCorrectText : String
var tResult : [GrammarRes]
}

View File

@ -0,0 +1,8 @@
//
// ColorToString.swift
// AIGrammar
//
// Created by oscar on 2024/3/28.
//
import Foundation

View File

@ -0,0 +1,8 @@
//
// CommFunc.swift
// AIGrammar
//
// Created by oscar on 2024/8/5.
//
import Foundation

View File

@ -0,0 +1,8 @@
//
// Iap.swift
// AIGrammar
//
// Created by oscar on 2024/7/3.
//
import Foundation

View File

@ -0,0 +1,10 @@
//
// InitAPP.swift
// AIGrammar
//
// Created by oscar on 2024/6/26.
//
import Foundation

View File

@ -0,0 +1,8 @@
//
// LogManager.swift
// AIGrammar
//
// Created by oscar on 2024/7/3.
//
import Foundation

View File

@ -0,0 +1,233 @@
//
// request.swift
// AIGrammar
//
// Created by oscar on 2024/6/4.
//
import Foundation
import Alamofire
import SwiftJWT
import SwiftyBeaver
struct MyClaims: Claims {
var deviceID: String
var gid: Int
var exp1: Int
}
struct APIResponse<T: Decodable>: Decodable {
let ret: Int
let message: String
let data: T?
}
struct Translation: Identifiable, Codable, Equatable {
var id = UUID()
var input: String
var translation: String
}
struct TranslationResponse: Codable {
let translation: String
}
struct WordDetails {
var word: String = ""
var explanations: [String] = []
var phrases: [String] = []
var synonyms: [String] = []
}
struct WordDetailsResponse: Codable {
let word: String
let explain: [String]?
let phrase: [String]?
let sync: [String]?
// Mapping to WordDetails for use in UI
func toWordDetails() -> WordDetails {
return WordDetails(
word: word,
explanations: explain ?? [],
phrases: phrase ?? [],
synonyms: sync ?? []
)
}
}
struct VIPStatusResponse: Codable {
let id: Int
let userid: String
let username: String
let vip: Int
}
struct NetworkManager {
static let shared = NetworkManager()
private let jwtSecret = globalEnvironment.jwtSecret
//
func checkGrammar(inputText: String, completion: @escaping ([GrammarRes]?, Error?) -> Void) {
let url = globalEnvironment.grammarURL
let headers: HTTPHeaders = createAuthorizationHeader()
let parameters: [String: Any] = [
"input": inputText,
"lang": "eng"
]
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseDecodable(of: [GrammarRes].self) { response in
switch response.result {
case .success(let results):
completion(results, nil)
case .failure(let error):
completion(nil, error)
}
}
}
//
func fetchWordDetails(inputText: String, lang: String = "eng", completion: @escaping (Result<WordDetails, Error>) -> Void) {
guard !inputText.isEmpty else { return }
let parameters: [String: Any] = ["input": inputText, "lang": lang]
let url = globalEnvironment.dictURL
let headers: HTTPHeaders = createAuthorizationHeader()
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseDecodable(of: WordDetailsResponse.self) { response in
switch response.result {
case .success(let detailsResponse):
print("Success: Received data for \(detailsResponse.word)")
let details = detailsResponse.toWordDetails() // Convert here
completion(.success(details))
case .failure(let error):
print("Error: \(error)")
print("Response Data: \(String(data: response.data ?? Data(), encoding: .utf8) ?? "No data")")
completion(.failure(error))
}
}
}
//
func translate(inputText: String, lang: String = "chs", completion: @escaping (Result<Translation, Error>) -> Void) {
guard !inputText.isEmpty else { return }
let url = globalEnvironment.translateURL
let parameters: [String: Any] = ["input": inputText, "lang": lang]
let headers: HTTPHeaders = createAuthorizationHeader()
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseDecodable(of: TranslationResponse.self) { response in
switch response.result {
case .success(let translationResponse):
let newTranslation = Translation(input: inputText, translation: translationResponse.translation)
completion(.success(newTranslation))
case .failure(let error):
completion(.failure(error))
}
}
}
//
func sendFeedback(input: String, output: String, isPositive: Bool) {
let url = globalEnvironment.feedbackURL
let parameters: [String: Any] = [
"product": "trans",
"input": input,
"output": output,
"res": isPositive ? "good" : "bad"
]
let headers: HTTPHeaders = createAuthorizationHeader()
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers).response { response in
switch response.result {
case .success(let data):
print("Feedback sent successfully: \(String(describing: data))")
case .failure(let error):
print("Error sending feedback: \(error)")
}
}
}
// VIP
func getUserProfile(completion: @escaping (Result<VIPStatusResponse, Error>) -> Void) {
let url = globalEnvironment.userURL
let parameters: [String: Any] = [:]
let headers: HTTPHeaders = createAuthorizationHeader()
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers, // 使JWT token
completion: { (result: Result<VIPStatusResponse, Error>) in
switch result {
case .success(let vipData):
logger.info("VIP Status: \(vipData.vip)")
completion(.success(vipData))
case .failure(let error):
logger.error("Error: \(error.localizedDescription)")
completion(.failure(error))
}
}
)
}
func performRequest<T: Decodable>(
endpoint: String,
parameters: Parameters, // 使 Alamofire Parameters
method: HTTPMethod = .post,
encoding: URLEncoding = .httpBody,
headers: HTTPHeaders? = nil,
completion: @escaping (Result<T, Error>) -> Void
) {
let url = endpoint
let defaultHeaders: HTTPHeaders = [.contentType("application/x-www-form-urlencoded")]
let combinedHeaders = headers ?? defaultHeaders
//
AF.request(url, method: .post, parameters: parameters, encoding: encoding, headers: combinedHeaders).responseDecodable(of: APIResponse<T>.self) { response in
switch response.result {
case .success(let apiResponse):
if apiResponse.ret == 0, let data = apiResponse.data {
completion(.success(data))
} else {
let error = NSError(domain: "", code: apiResponse.ret, userInfo: [NSLocalizedDescriptionKey: apiResponse.message])
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
private func createAuthorizationHeader() -> HTTPHeaders {
// Generate JWT and return headers
guard let jwtToken = generateJWT(deviceID: globalEnvironment.deviceID, gID: globalEnvironment.GID, jwtSecret: jwtSecret) else {
return [:]
}
return ["Authorization": "Bearer \(jwtToken)"]
}
private func generateJWT(deviceID: String, gID: Int, jwtSecret: String) -> String? {
let claims = MyClaims(deviceID: deviceID, gid: gID, exp1: Int(Date().timeIntervalSince1970) + 86400)
var jwt = JWT(claims: claims)
do {
let jwtSigner = JWTSigner.hs256(key: Data(jwtSecret.utf8))
let signedJWT = try jwt.sign(using: jwtSigner)
return signedJWT
} catch {
logger.error("JWT encoding failed with error: \(error)")
return nil
}
}
}