Compare commits
2 Commits
8c4713d92e
...
1026bbb987
| Author | SHA1 | Date | |
|---|---|---|---|
| 1026bbb987 | |||
| ea4a5617ec |
@ -3,7 +3,7 @@ import Foundation
|
|||||||
/// API 配置信息
|
/// API 配置信息
|
||||||
public enum APIConfig {
|
public enum APIConfig {
|
||||||
/// API 基础 URL
|
/// API 基础 URL
|
||||||
public static let baseURL = "https://api.memorywake.com:31274/api/v1"
|
public static let baseURL = "https://api.memorywake.com/api/v1"
|
||||||
|
|
||||||
/// 认证 token - 从 Keychain 中获取
|
/// 认证 token - 从 Keychain 中获取
|
||||||
public static var authToken: String {
|
public static var authToken: String {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import Foundation
|
|||||||
//enum AnyCodable: Codable {}
|
//enum AnyCodable: Codable {}
|
||||||
|
|
||||||
func passwordLogin(username: String, password: String) {
|
func passwordLogin(username: String, password: String) {
|
||||||
guard let url = URL(string: "https://api.memorywake.com:31274/api/v1/iam/login/password-login") else {
|
guard let url = URL(string: "https://api.memorywake.com/api/v1/iam/login/password-login") else {
|
||||||
print("❌ 无效的URL")
|
print("❌ 无效的URL")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,19 @@ extension NetworkService: NetworkServiceProtocol {
|
|||||||
post(path: path, parameters: parameters, headers: headers, completion: completion)
|
post(path: path, parameters: parameters, headers: headers, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getWithToken<T: Decodable>(
|
||||||
|
path: String,
|
||||||
|
parameters: [String: Any]? = nil,
|
||||||
|
completion: @escaping (Result<T, NetworkError>) -> Void
|
||||||
|
) {
|
||||||
|
var headers = [String: String]()
|
||||||
|
if let token = KeychainHelper.getAccessToken() {
|
||||||
|
headers["Authorization"] = "Bearer \(token)"
|
||||||
|
}
|
||||||
|
|
||||||
|
get(path: path, parameters: parameters, headers: headers, completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func upload(
|
public func upload(
|
||||||
request: URLRequest,
|
request: URLRequest,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ struct LoginResponse: Codable {
|
|||||||
|
|
||||||
func callLoginAPI() {
|
func callLoginAPI() {
|
||||||
// 1. 创建 URL
|
// 1. 创建 URL
|
||||||
let urlString = "https://api.memorywake.com:31274/api/v1/iam/login/password-login"
|
let urlString = "https://api.memorywake.com/api/v1/iam/login/password-login"
|
||||||
guard let url = URL(string: urlString) else {
|
guard let url = URL(string: urlString) else {
|
||||||
print("Invalid URL")
|
print("Invalid URL")
|
||||||
return
|
return
|
||||||
|
|||||||
47
wake/View/BlindBox/BlindBoxControls.swift
Normal file
47
wake/View/BlindBox/BlindBoxControls.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// MARK: - Blind Box Controls
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct BlindBoxControls: View {
|
||||||
|
let mediaType: BlindBoxMediaType
|
||||||
|
let animationPhase: BlindBoxAnimationPhase
|
||||||
|
let countdown: (minutes: Int, seconds: Int, milliseconds: Int)
|
||||||
|
let onButtonTap: () -> Void
|
||||||
|
let onCountdownStart: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if mediaType == .all {
|
||||||
|
Button(action: onButtonTap) {
|
||||||
|
if animationPhase == .loading {
|
||||||
|
Text("Next: \(countdown.minutes):\(String(format: "%02d", countdown.seconds)).\(String(format: "%02d", countdown.milliseconds))")
|
||||||
|
.font(Typography.font(for: .body))
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.white)
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.cornerRadius(32)
|
||||||
|
.onAppear(perform: onCountdownStart)
|
||||||
|
} else if animationPhase == .ready {
|
||||||
|
Text("Ready")
|
||||||
|
.font(Typography.font(for: .body))
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.themePrimary)
|
||||||
|
.foregroundColor(Color.themeTextMessageMain)
|
||||||
|
.cornerRadius(32)
|
||||||
|
} else {
|
||||||
|
Text("Go to Buy")
|
||||||
|
.font(Typography.font(for: .body))
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.themePrimary)
|
||||||
|
.foregroundColor(Color.themeTextMessageMain)
|
||||||
|
.cornerRadius(32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
wake/View/BlindBox/MediaOverlayView.swift
Normal file
76
wake/View/BlindBox/MediaOverlayView.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// MARK: - Media Overlay View
|
||||||
|
import SwiftUI
|
||||||
|
import AVKit
|
||||||
|
|
||||||
|
struct MediaOverlayView: View {
|
||||||
|
@Binding var videoPlayer: AVPlayer?
|
||||||
|
@Binding var showControls: Bool
|
||||||
|
let mediaType: BlindBoxMediaType
|
||||||
|
let displayImage: UIImage?
|
||||||
|
let scaledWidth: CGFloat
|
||||||
|
let scaledHeight: CGFloat
|
||||||
|
let scale: CGFloat
|
||||||
|
let videoURL: String
|
||||||
|
let imageURL: String
|
||||||
|
let blindGenerate: BlindBoxData?
|
||||||
|
let onBackTap: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialLight))
|
||||||
|
.opacity(0.3)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
|
||||||
|
Group {
|
||||||
|
if mediaType == .video, let player = videoPlayer {
|
||||||
|
AVPlayerController(player: $videoPlayer)
|
||||||
|
.frame(width: scaledWidth, height: scaledHeight)
|
||||||
|
.opacity(scale == 1 ? 1 : 0.7)
|
||||||
|
.onAppear { player.play() }
|
||||||
|
} else if mediaType == .image, let image = displayImage {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: scaledWidth, height: scaledHeight)
|
||||||
|
.opacity(scale == 1 ? 1 : 0.7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation(.easeInOut(duration: 0.1)) {
|
||||||
|
showControls.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
if showControls {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Button(action: onBackTap) {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
.font(.system(size: 24))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.padding(.top, 50)
|
||||||
|
.padding(.leading, 20)
|
||||||
|
.zIndex(1000)
|
||||||
|
.transition(.opacity)
|
||||||
|
.onAppear {
|
||||||
|
// 2秒后显示按钮
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
|
withAnimation(.easeInOut(duration: 0.3)) {
|
||||||
|
// 这里应该设置 showControls = true,但由于是绑定,我们假设调用方会处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.animation(.easeInOut(duration: 1.0), value: scale)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user