feat: 会员弹窗
This commit is contained in:
parent
8044927b51
commit
10ecd93b54
9
wake/Assets/Svg/IP1.svg
Normal file
9
wake/Assets/Svg/IP1.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 183 KiB |
11
wake/Assets/Svg/JoinList.svg
Normal file
11
wake/Assets/Svg/JoinList.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_6596_2873)">
|
||||
<rect x="0.0969238" y="0.567871" width="30.9623" height="30.9623" rx="15.4812" transform="rotate(0.179418 0.0969238 0.567871)" fill="black"/>
|
||||
<path d="M15.578 0.597365L28.9286 23.8893L2.08191 23.8052L15.578 0.597365Z" fill="#D9D9D9"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6596_2873">
|
||||
<rect x="0.0969238" y="0.567871" width="30.9623" height="30.9623" rx="15.4812" transform="rotate(0.179418 0.0969238 0.567871)" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
@ -26,6 +26,7 @@ enum TypographyStyle {
|
||||
case largeTitle // 大标题
|
||||
case smallLargeTitle // 小大标题
|
||||
case headline // 大标题
|
||||
case headline1 // 大标题1
|
||||
case title // 标题
|
||||
case title2 // 标题
|
||||
case title3 // 标题
|
||||
@ -54,6 +55,7 @@ struct Typography {
|
||||
private static let styleConfig: [TypographyStyle: TypographyConfig] = [
|
||||
.largeTitle: TypographyConfig(size: 32, weight: .heavy, textStyle: .largeTitle),
|
||||
.smallLargeTitle: TypographyConfig(size: 30, weight: .heavy, textStyle: .largeTitle),
|
||||
.headline1: TypographyConfig(size: 26, weight: .bold, textStyle: .headline),
|
||||
.headline: TypographyConfig(size: 24, weight: .bold, textStyle: .headline),
|
||||
.title3: TypographyConfig(size: 22, weight: .semibold, textStyle: .title2),
|
||||
.title: TypographyConfig(size: 20, weight: .semibold, textStyle: .title2),
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// 包装UIImageView以支持APNG动画
|
||||
struct APNGView: UIViewRepresentable {
|
||||
let imageName: String
|
||||
@Binding var isAnimating: Bool
|
||||
|
||||
func makeUIView(context: Context) -> UIImageView {
|
||||
let imageView = UIImageView()
|
||||
|
||||
// 从资源加载APNG
|
||||
if let image = UIImage(named: imageName) {
|
||||
imageView.image = image
|
||||
// 启用动画
|
||||
imageView.animationImages = image.images
|
||||
// 设置动画时长(根据实际帧数调整)
|
||||
imageView.animationDuration = image.duration
|
||||
// 根据isAnimating状态决定是否开始动画
|
||||
if isAnimating {
|
||||
imageView.startAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
return imageView
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIImageView, context: Context) {
|
||||
// 根据isAnimating状态控制动画
|
||||
if isAnimating {
|
||||
if !uiView.isAnimating {
|
||||
uiView.startAnimating()
|
||||
}
|
||||
} else {
|
||||
uiView.stopAnimating()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ struct BlindOutcomeView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@State private var isFullscreen = false
|
||||
@State private var isPlaying = false
|
||||
@State private var showIPListModal = false
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
@ -99,9 +100,11 @@ struct BlindOutcomeView: View {
|
||||
// Button below media
|
||||
VStack(spacing: 16) {
|
||||
Button(action: {
|
||||
// 如果携带的类型是video跳转到contentview
|
||||
// 如果携带的类型是video显示弹窗
|
||||
if case .video = media {
|
||||
// Router.shared.navigate(to: .mediaUpload)
|
||||
withAnimation {
|
||||
showIPListModal = true
|
||||
}
|
||||
} else {
|
||||
Router.shared.navigate(to: .mediaUpload)
|
||||
}
|
||||
@ -130,6 +133,9 @@ struct BlindOutcomeView: View {
|
||||
FullscreenMediaView(media: media, isPresented: $isFullscreen, isPlaying: $isPlaying, player: nil)
|
||||
}
|
||||
}
|
||||
.overlay(
|
||||
JoinModal(isPresented: $showIPListModal)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle()) // 确保在iPad上也能正确显示
|
||||
.navigationBarHidden(true) // 额外确保隐藏导航栏
|
||||
|
||||
220
wake/View/Blind/JoinModal.swift
Normal file
220
wake/View/Blind/JoinModal.swift
Normal file
@ -0,0 +1,220 @@
|
||||
import SwiftUI
|
||||
|
||||
struct JoinModal: View {
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .bottom) {
|
||||
// Semi-transparent background
|
||||
if isPresented {
|
||||
Color.black.opacity(0.4)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modal content
|
||||
if isPresented {
|
||||
VStack(spacing: 0) {
|
||||
// IP Image peeking from top
|
||||
HStack {
|
||||
// Make sure you have an image named "IP" in your assets
|
||||
SVGImage(svgName: "IP1")
|
||||
.frame(width: 116, height: 65)
|
||||
.offset(x: 30)
|
||||
Spacer()
|
||||
}
|
||||
.frame(height: 65)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
// Close button on the right
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isPresented = false
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 20, weight: .medium))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
.padding(12)
|
||||
}
|
||||
.padding(.trailing, 16)
|
||||
}
|
||||
|
||||
// 文本
|
||||
VStack(spacing: 8) {
|
||||
Text("Join us!")
|
||||
.font(Typography.font(for: .headline1, family: .quicksandBold))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
Text("Join us to get more exclusive benefits.")
|
||||
.font(.system(size: 14, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
// List content
|
||||
VStack (alignment: .leading) {
|
||||
HStack {
|
||||
SVGImage(svgName: "JoinList")
|
||||
.frame(width: 32, height: 32)
|
||||
HStack (alignment: .top){
|
||||
Text("Unlimited")
|
||||
.font(.system(size: 16, weight: .bold))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
Text(" blind box purchases.")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
.padding(.leading,12)
|
||||
HStack (alignment: .center) {
|
||||
SVGImage(svgName: "JoinList")
|
||||
.frame(width: 32, height: 32)
|
||||
VStack (alignment: .leading,spacing: 4) {
|
||||
HStack {
|
||||
Text("Freely")
|
||||
.font(.system(size: 16, weight: .bold))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
Text(" upload image and video")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
Text(" materials.")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
.padding(.leading,12)
|
||||
|
||||
HStack(alignment: .top) {
|
||||
SVGImage(svgName: "JoinList")
|
||||
.frame(width: 32, height: 32)
|
||||
VStack (alignment: .leading,spacing: 4) {
|
||||
HStack {
|
||||
Text("500")
|
||||
.font(.system(size: 16, weight: .bold))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
Text(" credits daily,")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
HStack(alignment: .top) {
|
||||
VStack (alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text("5000")
|
||||
.font(.system(size: 16, weight: .bold))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
Text(" permanent credits on your first")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
Text(" purchase!")
|
||||
.font(.system(size: 16, weight: .regular))
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, 12)
|
||||
.padding(.leading,12)
|
||||
HStack {
|
||||
Spacer() // This will push the button to the right
|
||||
Button(action: {
|
||||
// 点击跳转到会员页面
|
||||
Router.shared.navigate(to: .subscribe)
|
||||
}) {
|
||||
HStack {
|
||||
Text("See More")
|
||||
.font(.system(size: 16))
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 24)
|
||||
.cornerRadius(20)
|
||||
}
|
||||
}
|
||||
.padding(.trailing, 16) // Add some right padding to match the design
|
||||
Button(action: {
|
||||
// 点击跳转到会员页面
|
||||
Router.shared.navigate(to: .subscribe)
|
||||
}) {
|
||||
HStack {
|
||||
Text("Subscribe")
|
||||
.font(Typography.font(for: .body, family: .quicksandBold))
|
||||
Spacer()
|
||||
Text("$1.00/Mon")
|
||||
.font(Typography.font(for: .body, family: .quicksandBold))
|
||||
}
|
||||
.foregroundColor(.themeTextMessageMain)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 30)
|
||||
.background(Color.themePrimary)
|
||||
.cornerRadius(20)
|
||||
}
|
||||
.padding(.top, 16)
|
||||
// 协议条款
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
// Action for Terms of Service
|
||||
}) {
|
||||
Text("Terms of Service")
|
||||
.font(.system(size: 12, weight: .regular))
|
||||
.foregroundColor(.themeTextMessage)
|
||||
.underline() // Add underline
|
||||
}
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.5))
|
||||
.frame(width: 1, height: 16)
|
||||
.padding(.vertical, 4)
|
||||
Button(action: {
|
||||
// Action for Privacy Policy
|
||||
}) {
|
||||
Text("Privacy Policy")
|
||||
.font(.system(size: 12, weight: .regular))
|
||||
.foregroundColor(.themeTextMessage)
|
||||
.underline() // Add underline
|
||||
}
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.5))
|
||||
.frame(width: 1, height: 16)
|
||||
.padding(.vertical, 4)
|
||||
Button(action: {
|
||||
// Action for Restore Purchase
|
||||
}) {
|
||||
Text("Restore Purchase")
|
||||
.font(.system(size: 12, weight: .regular))
|
||||
.foregroundColor(.themeTextMessage)
|
||||
.underline() // Add underline
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 24)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
.background(Color.white)
|
||||
.cornerRadius(20, corners: [.topLeft, .topRight])
|
||||
}
|
||||
.frame(height: nil)
|
||||
.transition(.move(edge: .bottom))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.animation(.easeInOut, value: isPresented)
|
||||
}
|
||||
}
|
||||
|
||||
struct JoinModal_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
JoinModal(isPresented: .constant(true))
|
||||
}
|
||||
}
|
||||
@ -167,7 +167,7 @@ struct MediaUploadView: View {
|
||||
}
|
||||
|
||||
// 开始上传
|
||||
uploadManager.startUpload()
|
||||
// uploadManager.startUpload()
|
||||
} else {
|
||||
print("ℹ️ 没有新文件需要添加,所有选择的文件都已存在")
|
||||
}
|
||||
@ -249,7 +249,7 @@ struct MediaUploadView: View {
|
||||
}
|
||||
|
||||
// 开始上传新添加的媒体
|
||||
uploadManager.startUpload()
|
||||
// uploadManager.startUpload()
|
||||
print("媒体添加完成,总数量: \(uploadManager.selectedMedia.count)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ struct WakeApp: App {
|
||||
// 根据登录状态显示不同视图
|
||||
if authState.isAuthenticated {
|
||||
// 已登录:显示userInfo页面
|
||||
LoginView()
|
||||
MediaUploadView()
|
||||
.environmentObject(authState)
|
||||
// ContentView()
|
||||
// .environmentObject(authState)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user