Compare commits
2 Commits
8c4713d92e
...
1026bbb987
| Author | SHA1 | Date | |
|---|---|---|---|
| 1026bbb987 | |||
| ea4a5617ec |
@ -3,7 +3,7 @@ import Foundation
|
||||
/// API 配置信息
|
||||
public enum APIConfig {
|
||||
/// 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 中获取
|
||||
public static var authToken: String {
|
||||
|
||||
@ -11,7 +11,7 @@ import Foundation
|
||||
//enum AnyCodable: Codable {}
|
||||
|
||||
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")
|
||||
return
|
||||
}
|
||||
|
||||
@ -48,6 +48,19 @@ extension NetworkService: NetworkServiceProtocol {
|
||||
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
|
||||
public func upload(
|
||||
request: URLRequest,
|
||||
|
||||
@ -7,7 +7,7 @@ struct LoginResponse: Codable {
|
||||
|
||||
func callLoginAPI() {
|
||||
// 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 {
|
||||
print("Invalid URL")
|
||||
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