add swiftUI code
This commit is contained in:
@@ -0,0 +1,693 @@
|
||||
//
|
||||
// Login.swift
|
||||
// LoginKit
|
||||
//
|
||||
// Created by Balaji on 04/08/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
import Crisp
|
||||
|
||||
// MARK: - Errors
|
||||
struct Errors: Codable {
|
||||
let email: [String]?
|
||||
}
|
||||
|
||||
|
||||
struct LoginResponseSuccess: Codable {
|
||||
let data: LoginResponseSuccessClass?
|
||||
let message: String?
|
||||
let status: String?
|
||||
let errors: Errors?
|
||||
}
|
||||
|
||||
// MARK: - DataClass
|
||||
struct LoginResponseSuccessClass: Codable {
|
||||
let token: String
|
||||
let isAdmin: Int?
|
||||
let authData: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case token
|
||||
case isAdmin = "is_admin"
|
||||
case authData = "auth_data"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Login: View {
|
||||
|
||||
@Binding var isLoggedIn: Bool
|
||||
|
||||
@Binding var showSignup: Bool
|
||||
|
||||
/// View Properties
|
||||
@State private var emailID: String = ""
|
||||
|
||||
@State private var password: String = ""
|
||||
@State private var showForgotPasswordView: Bool = false
|
||||
/// Reset Password View (with New Password and Confimration Password View)
|
||||
@State private var showResetView: Bool = false
|
||||
/// Optional, Present If you want to ask OTP for login
|
||||
@State private var askOTP: Bool = false
|
||||
@State private var otpText: String = ""
|
||||
|
||||
@State private var showKefuView: Bool = false
|
||||
|
||||
|
||||
@State private var userInfo: UserInfo? = nil
|
||||
@State private var isLoading = false
|
||||
@State private var errorMessage: String?
|
||||
@State private var alert: Alert?
|
||||
@State private var okMessage: String?
|
||||
|
||||
@FocusState private var focusedField: Bool
|
||||
|
||||
@State private var isresetpwdLoading = false
|
||||
@State private var resetpwdErrorMessage = ""
|
||||
|
||||
@AppStorage("isFirstOpen") private var isFirstOpen: Bool?
|
||||
|
||||
@State var onboardingItems: [OnboardingItem] = [
|
||||
.init(title: "最稳定的加速器",
|
||||
subTitle: "順暢瀏覽社交媒體,同時支持Netflix及Youtube等流媒體內容平台,4K清晰度不卡頓。",
|
||||
lottieView: .init(name: "12c853b3",bundle: .main)),
|
||||
.init(title: "永不跑路的VPN加速器",
|
||||
subTitle: "一鍵訪問匿名互聯網,告別繁瑣的配置,跨平臺設備支持,隨時隨地連接網絡。",
|
||||
lottieView: .init(name: "2c630b55",bundle: .main)),
|
||||
.init(title: "加密访问,全球畅连",
|
||||
subTitle: "隱藏您的IP地址。互聯網流量通過TLS加密保護免受任何黑客探測、監控和攻擊。",
|
||||
lottieView: .init(name: "5a519db7",bundle: .main))
|
||||
]
|
||||
// MARK: Current Slide Index
|
||||
@State var currentIndex: Int = 0
|
||||
|
||||
var onBoardView: some View {
|
||||
|
||||
GeometryReader{
|
||||
let size = $0.size
|
||||
VStack(spacing: 40, content: {
|
||||
Spacer().frame(height: 10)
|
||||
|
||||
HStack(spacing: 0){
|
||||
ForEach($onboardingItems) { $item in
|
||||
let isLastSlide = (currentIndex == onboardingItems.count - 1)
|
||||
VStack{
|
||||
// MARK: Top Nav Bar
|
||||
HStack{
|
||||
Button(action: {
|
||||
if currentIndex > 0{
|
||||
currentIndex -= 1
|
||||
playAnimation()
|
||||
}
|
||||
}, label: {
|
||||
Image(systemName: "arrow.backward")
|
||||
}).tint(Color("Main"))
|
||||
.opacity(currentIndex > 0 ? 1 : 0)
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
// Button("跳过"){
|
||||
// currentIndex = onboardingItems.count - 1
|
||||
// playAnimation()
|
||||
// }
|
||||
// .opacity(isLastSlide ? 0 : 1)
|
||||
}
|
||||
.animation(.easeInOut, value: currentIndex)
|
||||
.tint(Color("Main"))
|
||||
//.fontWeight(.bold)
|
||||
|
||||
// MARK: Movable Slides
|
||||
VStack(spacing: 15){
|
||||
let offset = -CGFloat(currentIndex) * size.width
|
||||
// MARK: Resizable Lottie View
|
||||
ResizableLottieView(onboardingItem: $item)
|
||||
.frame(height: size.width*0.8)
|
||||
.onAppear {
|
||||
// MARK: Intially Playing First Slide Animation
|
||||
if currentIndex == indexOf(item){
|
||||
item.lottieView.play(toProgress: 0.7)
|
||||
}
|
||||
}
|
||||
.offset(x: offset)
|
||||
.animation(.easeInOut(duration: 0.5), value: currentIndex)
|
||||
|
||||
Text(item.title)
|
||||
.font(.title.bold())
|
||||
.offset(x: offset)
|
||||
.animation(.easeInOut(duration: 0.5).delay(0.1), value: currentIndex)
|
||||
|
||||
Text(item.subTitle)
|
||||
.font(.system(size: 14))
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal,15)
|
||||
.foregroundColor(.gray)
|
||||
.offset(x: offset)
|
||||
.animation(.easeInOut(duration: 0.5).delay(0.2), value: currentIndex)
|
||||
}
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
// MARK: Next / Login Button
|
||||
VStack(spacing: 15){
|
||||
Button(isLastSlide ? "登录" : "下一步"){
|
||||
if currentIndex < onboardingItems.count - 1{
|
||||
// MARK: Pausing Previous Animation
|
||||
let currentProgress = onboardingItems[currentIndex].lottieView.currentProgress
|
||||
onboardingItems[currentIndex].lottieView.currentProgress = (currentProgress == 0 ? 0.7 : currentProgress)
|
||||
currentIndex += 1
|
||||
// MARK: Playing Next Animation from Start
|
||||
playAnimation()
|
||||
}
|
||||
if isLastSlide {
|
||||
withAnimation(){
|
||||
isFirstOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
// .fontWeight(.bold)
|
||||
.foregroundColor(.white)
|
||||
.padding(.vertical,isLastSlide ? 13 : 12)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background {
|
||||
Capsule()
|
||||
.fill(Color("Main2"))
|
||||
}
|
||||
.padding(.horizontal,isLastSlide ? 50 : 100)
|
||||
|
||||
|
||||
if currentIndex == onboardingItems.count - 1{
|
||||
|
||||
HStack{
|
||||
// Button("Terms of Service"){}
|
||||
|
||||
// Button("Privacy Policy"){}
|
||||
}
|
||||
.font(.caption2)
|
||||
//.underline(true, color: .primary)
|
||||
.offset(y: 5)
|
||||
}
|
||||
}.onTapGesture {
|
||||
if currentIndex < onboardingItems.count - 1{
|
||||
// MARK: Pausing Previous Animation
|
||||
let currentProgress = onboardingItems[currentIndex].lottieView.currentProgress
|
||||
onboardingItems[currentIndex].lottieView.currentProgress = (currentProgress == 0 ? 0.7 : currentProgress)
|
||||
currentIndex += 1
|
||||
// MARK: Playing Next Animation from Start
|
||||
playAnimation()
|
||||
}
|
||||
if isLastSlide {
|
||||
withAnimation(){
|
||||
isFirstOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.animation(.easeInOut, value: isLastSlide)
|
||||
.padding(15)
|
||||
.frame(width: size.width, height: size.height)
|
||||
}
|
||||
}
|
||||
.frame(width: size.width * CGFloat(onboardingItems.count),alignment: .leading)
|
||||
}).ignoresSafeArea(.all)
|
||||
|
||||
// if !#available(iOS 15.0, *) {
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
func playAnimation(){
|
||||
onboardingItems[currentIndex].lottieView.currentProgress = 0
|
||||
onboardingItems[currentIndex].lottieView.play(toProgress: 0.7)
|
||||
}
|
||||
|
||||
// MARK: Retreving Index of the Item in the Array
|
||||
func indexOf(_ item: OnboardingItem)->Int{
|
||||
if let index = onboardingItems.firstIndex(of: item){
|
||||
return index
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if isFirstOpen ?? true {
|
||||
onBoardView
|
||||
}else{
|
||||
loginView
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var loginView: some View {
|
||||
VStack(alignment: .leading, spacing: 15, content: {
|
||||
Spacer(minLength: 0)
|
||||
|
||||
// HStack{
|
||||
// Spacer()
|
||||
// Image("applogo").resizable().frame(width: 100, height: 100).cornerRadius(20)
|
||||
// Spacer()
|
||||
// }
|
||||
|
||||
Text("登录")
|
||||
.font(.largeTitle)
|
||||
//.fontWeight(.heavy)
|
||||
|
||||
Text("请按登录按钮继续")
|
||||
.font(.callout)
|
||||
//.fontWeight(.semibold)
|
||||
.foregroundStyle(.gray)
|
||||
.padding(.top, -5)
|
||||
|
||||
VStack(spacing: 25) {
|
||||
/// Custom Text Fields
|
||||
CustomTF(sfIcon: "at", hint: "Email", value: $emailID)
|
||||
|
||||
CustomTF(sfIcon: "lock", hint: "密码", isPassword: true, value: $password)
|
||||
.padding(.top, 5).focused($focusedField)
|
||||
|
||||
|
||||
HStack(content: {
|
||||
|
||||
Button("忘记密码?") {
|
||||
showForgotPasswordView.toggle()
|
||||
}
|
||||
.font(.callout)
|
||||
//.fontWeight(.heavy)
|
||||
.tint(.white)
|
||||
|
||||
Button {
|
||||
showKefuView.toggle()
|
||||
} label: {
|
||||
|
||||
Text("联系客服") .font(.callout)
|
||||
Image(systemName: "person.crop.circle.badge.questionmark")
|
||||
}.tint(.white)
|
||||
})
|
||||
.hSpacing(.trailing)
|
||||
|
||||
|
||||
|
||||
/// Login Button
|
||||
///
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
}
|
||||
|
||||
if let errorMessage = errorMessage {
|
||||
Text(errorMessage)
|
||||
.bold()
|
||||
.foregroundColor(.red)
|
||||
.multilineTextAlignment(.center) // 确保多行文本居中
|
||||
.padding()
|
||||
}
|
||||
|
||||
if let okmsg = okMessage {
|
||||
Text(okmsg)
|
||||
.bold()
|
||||
.foregroundColor(Color("Main"))
|
||||
.multilineTextAlignment(.center) // 确保多行文本居中
|
||||
.padding()
|
||||
}
|
||||
|
||||
if let userInfo = userInfo {
|
||||
Text("Logged in as: \(userInfo.data.email)")
|
||||
// AsyncImage(url: URL(string: userInfo.data.avatar_url))
|
||||
// .frame(width: 64, height: 64)
|
||||
// .clipShape(Circle())
|
||||
}
|
||||
|
||||
GradientButton(title: "登录", icon: "arrow.right") {
|
||||
/// YOUR CODE
|
||||
//askOTP.toggle()
|
||||
Task{
|
||||
await loginUser()
|
||||
}
|
||||
}
|
||||
.hSpacing(.trailing)
|
||||
/// Disabling Until the Data is Entered
|
||||
.disableWithOpacity(emailID.isEmpty || password.isEmpty || isLoading)
|
||||
}
|
||||
.padding(.top, 20)
|
||||
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
HStack(spacing: 6) {
|
||||
Text("没有账号?")
|
||||
.foregroundStyle(.gray)
|
||||
|
||||
Button("注册") {
|
||||
showSignup.toggle()
|
||||
}
|
||||
//.fontWeight(.bold)
|
||||
.tint(.white)
|
||||
}
|
||||
.font(.callout)
|
||||
.hSpacing()
|
||||
})
|
||||
.onAppear(){
|
||||
//获取 config 信息
|
||||
Task{
|
||||
await getConfig()
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 15)
|
||||
.padding(.horizontal, 25)
|
||||
//.toolbar(.hidden, for: .navigationBar)
|
||||
/// Asking Email ID For Sending Reset Link
|
||||
.sheet(isPresented: $showForgotPasswordView, content: {
|
||||
if #available(iOS 16.4, *) {
|
||||
/// Since I wanted a Custom Sheet Corner Radius
|
||||
ForgotPassword(showResetView: $showResetView, emailID: $emailID, otpText: $otpText)
|
||||
.presentationDetents([.height(300)])
|
||||
.presentationCornerRadius(30)
|
||||
} else {
|
||||
ForgotPassword(showResetView: $showResetView, emailID: $emailID, otpText: $otpText)
|
||||
// .presentationDetents([.height(300)])
|
||||
}
|
||||
})
|
||||
.fullScreenCover(isPresented: $showKefuView, content: {
|
||||
SupportView()
|
||||
})
|
||||
/// Resetting New Password
|
||||
.sheet(isPresented: $showResetView, content: {
|
||||
if #available(iOS 16.4, *) {
|
||||
/// Since I wanted a Custom Sheet Corner Radius
|
||||
|
||||
PasswordResetView(isresetpwdLoading: $isresetpwdLoading, resetpwdErrorMessage: $resetpwdErrorMessage) { password in
|
||||
print(otpText + " " + password)
|
||||
|
||||
Task{
|
||||
await resetpwd(password:password)
|
||||
}
|
||||
}
|
||||
.presentationDetents([.height(350)])
|
||||
.presentationCornerRadius(30)
|
||||
} else {
|
||||
PasswordResetView(isresetpwdLoading: $isresetpwdLoading, resetpwdErrorMessage: $resetpwdErrorMessage){password in
|
||||
print(otpText + " " + password)
|
||||
|
||||
Task{
|
||||
await resetpwd(password:password)
|
||||
}
|
||||
}
|
||||
// .presentationDetents([.height(350)])
|
||||
}
|
||||
})
|
||||
.alertBinding($alert)
|
||||
/// OTP Prompt
|
||||
.sheet(isPresented: $askOTP, onDismiss: {
|
||||
/// YOUR CODE
|
||||
/// Reset OTP if You Want
|
||||
// otpText = ""
|
||||
}, content: {
|
||||
if #available(iOS 16.4, *) {
|
||||
/// Since I wanted a Custom Sheet Corner Radius
|
||||
OTPView(otpText: $otpText)
|
||||
.presentationDetents([.height(350)])
|
||||
.presentationCornerRadius(30)
|
||||
} else {
|
||||
OTPView(otpText: $otpText)
|
||||
//.presentationDetents([.height(350)])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func resetpwd(password:String) async {
|
||||
isresetpwdLoading = true
|
||||
let loginUrl = URL(string: "\(UserManager.shared.baseURL())passport/auth/forget?email=\(emailID)&password=\(password)&email_code=\(otpText)")!
|
||||
var request = URLRequest(url: loginUrl)
|
||||
request.httpMethod = "POST"
|
||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
|
||||
print(loginUrl)
|
||||
let loginData = ["email": emailID, "password": password, "email_code": otpText]
|
||||
guard let httpBody = try? JSONSerialization.data(withJSONObject: loginData) else {
|
||||
self.resetpwdErrorMessage = "请求格式错误"
|
||||
self.isresetpwdLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
request.httpBody = httpBody
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
DispatchQueue.main.async {
|
||||
isresetpwdLoading = false
|
||||
if let error = error {
|
||||
resetpwdErrorMessage = ( "密码重置失败: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
resetpwdErrorMessage = ("密码重置失败: 数据为空")
|
||||
return
|
||||
}
|
||||
|
||||
struct resetpwdResponseSuccess: Codable {
|
||||
let message: String?
|
||||
let status: String?
|
||||
}
|
||||
|
||||
// Parse the login response and get the authorization token
|
||||
if let jsonResponse = try? JSONDecoder().decode(resetpwdResponseSuccess.self, from: data){
|
||||
dump(jsonResponse)
|
||||
|
||||
if let status = jsonResponse.status , status == "success" {
|
||||
|
||||
withAnimation {
|
||||
showResetView = false
|
||||
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
|
||||
alert = Alert(okMessage: "密码重置成功!")
|
||||
}
|
||||
}else{
|
||||
if let message = jsonResponse.message {
|
||||
resetpwdErrorMessage = ( "\(message)")
|
||||
}else{
|
||||
resetpwdErrorMessage = ( "密码重置失败: 未知错误")
|
||||
}
|
||||
}
|
||||
}else{
|
||||
resetpwdErrorMessage = ("密码重置失败: 数据 json 格式错误")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
task.resume()
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Networking Logic
|
||||
|
||||
func getConfig() async {
|
||||
|
||||
let loginUrl = URL(string: UserManager.shared.configURL)!
|
||||
var request = URLRequest(url: loginUrl)
|
||||
request.httpMethod = "GET"
|
||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(Bundle.main.bundleIdentifier ?? "", forHTTPHeaderField: "bid")
|
||||
request.addValue(UserManager.shared.appversion, forHTTPHeaderField: "appver")
|
||||
await request.addValue(UIDevice.current.model, forHTTPHeaderField: "model")
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
DispatchQueue.main.async {
|
||||
if let _ = error {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
|
||||
Task{
|
||||
await getConfig()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
|
||||
Task{
|
||||
await getConfig()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Parse the login response and get the authorization token
|
||||
struct configReponse: Codable {
|
||||
let baseURL, mainregisterURL,paymentURL,crisptoken: String
|
||||
let telegramurl, kefuurl, websiteURL,baseDYURL: String
|
||||
let message: String
|
||||
let code: Int
|
||||
}
|
||||
|
||||
if let jsonString = String(data: data, encoding: .utf8) {
|
||||
print("Response data: \(jsonString)")
|
||||
}
|
||||
|
||||
if let jsonResponse = try? JSONDecoder().decode(configReponse.self, from: data){
|
||||
// dump(jsonResponse)
|
||||
if jsonResponse.code == 1 {
|
||||
//save data
|
||||
UserManager.shared.storebaseURLData(data: jsonResponse.baseURL)
|
||||
UserManager.shared.storemainregisterURLData(data: jsonResponse.mainregisterURL)
|
||||
UserManager.shared.storepaymentURLData(data: jsonResponse.paymentURL)
|
||||
UserManager.shared.storetelegramUrlData(data: jsonResponse.telegramurl)
|
||||
UserManager.shared.storekefuUrlData(data: jsonResponse.kefuurl)
|
||||
UserManager.shared.storewebsiteURLData(data: jsonResponse.websiteURL)
|
||||
UserManager.shared.storebaseDYURL(data: jsonResponse.baseDYURL)
|
||||
//crisptoken
|
||||
CrispSDK.configure(websiteID: jsonResponse.crisptoken)
|
||||
}
|
||||
}else{
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
|
||||
Task{
|
||||
await getConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
|
||||
func loginUser() async {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
focusedField = false
|
||||
let loginUrl = URL(string: "\(UserManager.shared.baseURL())passport/auth/login")!
|
||||
var request = URLRequest(url: loginUrl)
|
||||
request.httpMethod = "POST"
|
||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let loginData = ["email": emailID, "password": password, "captchaData": ""]
|
||||
guard let httpBody = try? JSONSerialization.data(withJSONObject: loginData) else {
|
||||
self.errorMessage = "Invalid login data"
|
||||
self.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
request.httpBody = httpBody
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
DispatchQueue.main.async {
|
||||
self.isLoading = false
|
||||
|
||||
if let error = error {
|
||||
self.errorMessage = "登录失败: \(error.localizedDescription)"
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
self.errorMessage = "登录失败: 数据为空"
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Parse the login response and get the authorization token
|
||||
if let jsonResponse = try? JSONDecoder().decode(LoginResponseSuccess.self, from: data){
|
||||
dump(jsonResponse)
|
||||
|
||||
if let _ = jsonResponse.errors, let message = jsonResponse.message{
|
||||
// Proceed to fetch user info
|
||||
// fetchUserInfo(token: token)
|
||||
self.errorMessage = "\(message)"
|
||||
}else{
|
||||
if let authData = jsonResponse.data?.authData {
|
||||
|
||||
UserManager.shared.updateLoginStatus(true)
|
||||
UserManager.shared.storeAutoData(data: authData)
|
||||
self.okMessage = "登录成功,正在跳转..."
|
||||
self.isLoading = true
|
||||
|
||||
|
||||
UserManager.shared.storeUserInfo(email: emailID, avator: "")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
|
||||
self.isLoggedIn = true
|
||||
}
|
||||
//去掉记录
|
||||
// Task{
|
||||
// await fetchUserInfo(token: authData)
|
||||
// }
|
||||
}else{
|
||||
if let message = jsonResponse.message {
|
||||
self.errorMessage = "\(message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
self.errorMessage = "登录失败: 数据 json 格式错误"
|
||||
}
|
||||
//{"data":{"token":"880e8785746c0bf72b2e01a882a678e7","is_admin":1,"auth_data":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Miwic2Vzc2lvbiI6Ijc4MzE1Yjc5YmNlNTliYWIzOTg5MTIzMTFhNDkwN2NiIn0.00fdwJ85bOSycfnkOPmhF7pSR1VV9WczBEeE9aXncQQ"}}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
func fetchUserInfo(token: String) async {
|
||||
let userInfoUrl = URL(string: "\(UserManager.shared.baseURL())user/info")!
|
||||
var request = URLRequest(url: userInfoUrl)
|
||||
request.httpMethod = "GET"
|
||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue("\(token)", forHTTPHeaderField: "Authorization")
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
self.errorMessage = "Failed to get user info: \(error.localizedDescription)"
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
self.errorMessage = "No user data received"
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Parse the user info response
|
||||
if let userInfo = try? JSONDecoder().decode(UserInfo.self, from: data) {
|
||||
self.userInfo = userInfo
|
||||
// Save user info to local storage
|
||||
|
||||
|
||||
UserManager.shared.saveUserInfoToLocal(userInfo: userInfo)
|
||||
UserManager.shared.storeUserInfo(email: userInfo.data.email, avator: userInfo.data.avatar_url)
|
||||
|
||||
|
||||
// 读取登录状态和用户信息
|
||||
let isLoggedIn = UserManager.shared.isUserLoggedIn()
|
||||
let userInfo = UserManager.shared.getUserInfo()
|
||||
let autoData = UserManager.shared.getAutoData()
|
||||
|
||||
print("Logged in: \(isLoggedIn)")
|
||||
print("Email: \(userInfo.email), avator: \(userInfo.avator)")
|
||||
print("Auto Data: \(autoData)")
|
||||
self.isLoggedIn = true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
// Save user info to local storage (UserDefaults)
|
||||
|
||||
/**
|
||||
{"data":{"email":"ceshi1@qq.com","transfer_enable":119185342464,"device_limit":null,"last_login_at":1725893596,"created_at":1725893596,"banned":0,"remind_expire":1,"remind_traffic":1,"expired_at":null,"balance":0,"commission_balance":0,"plan_id":1,"discount":null,"commission_rate":null,"telegram_id":null,"uuid":"0b0d55e3-9f6f-4ee0-9a73-2901655cfaf2","avatar_url":"https:\/\/cravatar.cn\/avatar\/845735bc7a6186ae5bad56e7cb87d88b?s=64&d=identicon"}}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user