import SwiftUI struct JoinModal: View { @Binding var isPresented: Bool let onClose: () -> Void 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) { // 顶部装饰图已移除(按需求) VStack(spacing: 0) { // Close button on the right HStack { Spacer() Button(action: { withAnimation { onClose() } }) { 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 { JoinListMark() .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) { JoinListMark() .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) { JoinListMark() .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 if let url = URL(string: "https://memorywake.com/privacy-policy") { UIApplication.shared.open(url) } }) { 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: { // 打开网页 if let url = URL(string: "https://memorywake.com/privacy-policy") { UIApplication.shared.open(url) } }) { 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 if let url = URL(string: "https://memorywake.com/privacy-policy") { UIApplication.shared.open(url) } }) { Text("AI Usage Guidelines") .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) } } // MARK: - SwiftUI JoinList 图标重绘 private struct JoinListMark: View { var body: some View { ZStack { // 背景圆 Circle() .fill( LinearGradient(colors: [Color.themePrimary.opacity(0.9), Color.orange.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing) ) .shadow(color: Color.black.opacity(0.12), radius: 4, x: 0, y: 2) // 右指三角(白色) GeometryReader { geo in Path { path in let w = geo.size.width let h = geo.size.height path.move(to: CGPoint(x: w*0.42, y: h*0.30)) path.addLine(to: CGPoint(x: w*0.70, y: h*0.50)) path.addLine(to: CGPoint(x: w*0.42, y: h*0.70)) path.closeSubpath() } .fill(Color.white) .opacity(0.95) } .padding(8) } } } struct JoinModal_Previews: PreviewProvider { static var previews: some View { JoinModal(isPresented: .constant(true), onClose: {}) } }