feat: 反馈页面

This commit is contained in:
jinyaqiu 2025-09-01 15:19:25 +08:00
parent 85bd75b03a
commit c5cb87b90b
4 changed files with 96 additions and 58 deletions

View File

@ -645,16 +645,14 @@ struct BlindBoxView: View {
Button(action: {
// BlindOutcomeView
if mediaType == .video, !videoURL.isEmpty, let url = URL(string: videoURL) {
Router.shared.navigate(to: .blindOutcome(media: .video(url, nil)))
Router.shared.navigate(to: .blindOutcome(media: .video(url, nil), time: blindGenerate?.videoGenerateTime ?? "hhsdshjsjdhn", description:blindGenerate?.description ?? "informationinformationinformationinformationinformationinformation"))
} else if mediaType == .image, let image = displayImage {
Router.shared.navigate(to: .blindOutcome(media: .image(image)))
Router.shared.navigate(to: .blindOutcome(media: .image(image), time: blindGenerate?.videoGenerateTime ?? "hhsdshjsjdhn", description:blindGenerate?.description ?? "informationinformationinformationinformationinformationinformation"))
}
}) {
Image(systemName: "chevron.left.circle.fill")
.font(.system(size: 36))
Image(systemName: "chevron.left")
.font(.system(size: 24))
.foregroundColor(.black)
.padding(12)
.clipShape(Circle())
}
Spacer()
}

View File

@ -8,13 +8,14 @@ enum AppRoute: Hashable {
case feedbackDetail(type: FeedbackView.FeedbackType)
case mediaUpload
case blindBox(mediaType: BlindBoxView.BlindBoxMediaType)
case blindOutcome(media: MediaType)
case blindOutcome(media: MediaType, time: String? = nil, description: String? = nil)
case memories
case subscribe
case userInfo
case account
case about
case permissionManagement
case feedback
@ViewBuilder
var view: some View {
@ -31,8 +32,8 @@ enum AppRoute: Hashable {
MediaUploadView()
case .blindBox(let mediaType):
BlindBoxView(mediaType: mediaType)
case .blindOutcome(let media):
BlindOutcomeView(media: media)
case .blindOutcome(let media, let time, let description):
BlindOutcomeView(media: media, time: time, description: description)
case .memories:
MemoriesView()
case .subscribe:
@ -45,6 +46,8 @@ enum AppRoute: Hashable {
AboutUsView()
case .permissionManagement:
PermissionManagementView()
case .feedback:
FeedbackView()
}
}
}

View File

@ -5,11 +5,19 @@ import os.log
/// A view that displays either an image or a video with fullscreen support
struct BlindOutcomeView: View {
let media: MediaType
let time: String?
let description: String?
@Environment(\.presentationMode) var presentationMode
@State private var isFullscreen = false
@State private var isPlaying = false
@State private var showIPListModal = false
init(media: MediaType, time: String? = nil, description: String? = nil) {
self.media = media
self.time = time
self.description = description
}
var body: some View {
NavigationView {
ZStack {
@ -54,51 +62,72 @@ struct BlindOutcomeView: View {
// Media content
GeometryReader { geometry in
ZStack {
//
RoundedRectangle(cornerRadius: 12)
.fill(Color.white)
.shadow(color: Color.black.opacity(0.1), radius: 8, x: 0, y: 2)
switch media {
case .image(let uiImage):
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.cornerRadius(10)
.padding(4)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.onTapGesture {
withAnimation {
isFullscreen.toggle()
VStack(spacing: 16) {
ZStack {
//
RoundedRectangle(cornerRadius: 12)
.fill(Color.white)
.shadow(color: Color.black.opacity(0.1), radius: 8, x: 0, y: 2)
VStack(spacing: 0) {
switch media {
case .image(let uiImage):
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.cornerRadius(10)
.padding(4)
.onTapGesture {
withAnimation {
isFullscreen.toggle()
}
}
case .video(let url, _):
// Create an AVPlayer with the video URL
let player = AVPlayer(url: url)
VideoPlayer(player: player)
.cornerRadius(10)
.padding(4)
.onAppear {
player.play()
isPlaying = true
}
.onDisappear {
player.pause()
isPlaying = false
}
}
VStack(alignment: .leading, spacing: 8) {
if let description = description, !description.isEmpty {
VStack(alignment: .leading, spacing: 2) {
Text("Description")
.font(Typography.font(for: .body, family: .quicksandBold))
.foregroundColor(.themeTextMessageMain)
Text(description)
.font(.system(size: 12))
.foregroundColor(Color.themeTextMessageMain)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.horizontal, 12)
.padding(.bottom, 12)
}
}
case .video(let url, _):
// Create an AVPlayer with the video URL
let player = AVPlayer(url: url)
VideoPlayer(player: player)
.cornerRadius(10)
.padding(4)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.onAppear {
player.play()
isPlaying = true
}
.onDisappear {
player.pause()
isPlaying = false
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.top, 8)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.padding(.bottom, 20)
}
.frame(height: UIScreen.main.bounds.height / 2)
.padding(.horizontal)
Spacer()
// Button below media
VStack(spacing: 16) {
// Button at bottom
VStack {
Spacer()
Button(action: {
// video
if case .video = media {
@ -106,7 +135,7 @@ struct BlindOutcomeView: View {
showIPListModal = true
}
} else {
Router.shared.navigate(to: .mediaUpload)
Router.shared.navigate(to: .feedbackView)
}
}) {
Text("Continue")
@ -117,9 +146,9 @@ struct BlindOutcomeView: View {
.background(Color.themePrimary)
.cornerRadius(26)
}
.padding()
.padding(.horizontal)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding(.bottom, 20)
}
}
.navigationBarHidden(true) //
@ -350,12 +379,20 @@ private struct VideoControls: View {
// MARK: - Preview
struct BlindOutcomeView_Previews: PreviewProvider {
static var previews: some View {
// Preview with image
BlindOutcomeView(media: .image(UIImage(systemName: "photo")!))
// Preview with image and details
BlindOutcomeView(
media: .image(UIImage(systemName: "photo")!),
time: "2:30",
description: "This is a sample description for the preview. It shows how the text will wrap and display below the media content."
)
// Preview with video
// Preview with video and details
if let url = URL(string: "https://example.com/sample.mp4") {
BlindOutcomeView(media: .video(url, nil))
BlindOutcomeView(
media: .video(url, nil),
time: "1:45",
description: "Video content with time and description"
)
}
}
}

View File

@ -58,7 +58,6 @@ struct FeedbackView: View {
VStack(spacing: 24) {
Text("How are you feeling?")
.font(Typography.font(for: .title2, family: .quicksandBold))
.fontWeight(.semibold)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
.padding(.bottom, 50)
@ -100,7 +99,6 @@ struct FeedbackView: View {
.padding(.vertical, 24)
.background(Color.white)
.cornerRadius(16)
.shadow(color: Color.black.opacity(0.2), radius: 12, x: 0, y: 4)
.padding(.horizontal, 16)
.frame(minHeight: geometry.size.height - 120) // Subtract navigation bar and bottom button height
@ -109,8 +107,8 @@ struct FeedbackView: View {
}
.frame(maxWidth: .infinity, minHeight: geometry.size.height - 44) // Subtract navigation bar height
}
.background(Color.themeTextWhiteSecondary)
}
.background(Color.themeTextWhiteSecondary) // Add background color to the GeometryReader
// Continue Button
@ -119,18 +117,20 @@ struct FeedbackView: View {
}) {
Text("Continue")
.font(.headline)
.foregroundColor(selectedFeedback != nil ? .white : .gray)
.foregroundColor(selectedFeedback != nil ? .themeTextMessageMain : .gray)
.frame(maxWidth: .infinity)
.frame(height: 56)
.background(
RoundedRectangle(cornerRadius: 25)
RoundedRectangle(cornerRadius: 32)
.fill(selectedFeedback != nil ?
Color.themePrimary : Color(.systemGray5))
Color.themePrimary : Color.themeTextWhiteSecondary)
)
}
.disabled(selectedFeedback == nil)
.padding()
.background(Color.themeTextWhiteSecondary) // Add background color to the button area
}
.background(Color.themeTextWhiteSecondary) // Set the background for the entire view
.navigationBarHidden(true)
}
}