Fix footer button overlap on small screens
This commit is contained in:
parent
45fd4c327b
commit
e3ab7d8e80
@ -96,7 +96,7 @@ private struct DashboardPromotionCard: View {
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
HStack(alignment: .top) {
|
||||
Text(badge.uppercased())
|
||||
.font(.system(size: 11, weight: .heavy))
|
||||
@ -106,9 +106,9 @@ private struct DashboardPromotionCard: View {
|
||||
.background(.white.opacity(0.2))
|
||||
.clipShape(Capsule())
|
||||
.foregroundColor(.white)
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
Image(systemName: accentSymbol)
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
@ -116,17 +116,17 @@ private struct DashboardPromotionCard: View {
|
||||
.background(.white.opacity(0.18))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||
}
|
||||
|
||||
|
||||
Text(title)
|
||||
.font(.system(size: 21, weight: .heavy, design: .rounded))
|
||||
.font(.system(size: 20, weight: .heavy, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
|
||||
Text(message)
|
||||
.font(.system(size: 13.5, weight: .medium))
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.foregroundColor(.white.opacity(0.85))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
|
||||
Button(action: action) {
|
||||
HStack(spacing: 6) {
|
||||
Text(actionTitle)
|
||||
@ -141,8 +141,8 @@ private struct DashboardPromotionCard: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(24)
|
||||
.frame(maxWidth: .infinity, minHeight: 200, alignment: .topLeading)
|
||||
.padding(18)
|
||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.fill(Self.defaultGradient)
|
||||
|
||||
@ -2,33 +2,32 @@ import SwiftUI
|
||||
|
||||
struct HelpAndResourcesSection: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 15) {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
Text("Help & Resources")
|
||||
.font(.system(size: 22, weight: .bold, design: .rounded))
|
||||
.font(.system(size: 20, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.primary.opacity(0.8))
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
resourceLink(
|
||||
icon: "sparkles",
|
||||
title: "Recommended Models",
|
||||
url: "https://tryvoiceink.com/recommended-models"
|
||||
)
|
||||
|
||||
|
||||
resourceLink(
|
||||
icon: "video.fill",
|
||||
title: "YouTube Videos & Guides",
|
||||
url: "https://www.youtube.com/@tryvoiceink/videos"
|
||||
)
|
||||
|
||||
|
||||
resourceLink(
|
||||
icon: "book.fill",
|
||||
title: "Documentation",
|
||||
url: "https://tryvoiceink.com/docs" // Placeholder
|
||||
url: "https://tryvoiceink.com/docs"
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(22)
|
||||
.padding(.vertical, 2)
|
||||
.padding(18)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.fill(Color(nsColor: .windowBackgroundColor))
|
||||
|
||||
@ -9,22 +9,28 @@ struct MetricsContent: View {
|
||||
if transcriptions.isEmpty {
|
||||
emptyStateView
|
||||
} else {
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
heroSection
|
||||
metricsSection
|
||||
HStack(alignment: .top, spacing: 18) {
|
||||
HelpAndResourcesSection()
|
||||
DashboardPromotionsSection(licenseState: licenseState)
|
||||
GeometryReader { geometry in
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
heroSection
|
||||
metricsSection
|
||||
HStack(alignment: .top, spacing: 18) {
|
||||
HelpAndResourcesSection()
|
||||
DashboardPromotionsSection(licenseState: licenseState)
|
||||
}
|
||||
|
||||
Spacer(minLength: 20)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
footerActionsView
|
||||
}
|
||||
}
|
||||
.frame(minHeight: geometry.size.height - 56)
|
||||
.padding(.vertical, 28)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
.padding(.vertical, 28)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
.background(Color(.windowBackgroundColor))
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
footerActionsView
|
||||
.padding()
|
||||
.background(Color(.windowBackgroundColor))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,26 +139,10 @@ struct MetricsContent: View {
|
||||
private var footerActionsView: some View {
|
||||
HStack(spacing: 12) {
|
||||
CopySystemInfoButton()
|
||||
feedbackButton
|
||||
FeedbackButton()
|
||||
}
|
||||
}
|
||||
|
||||
private var feedbackButton: some View {
|
||||
Button(action: {
|
||||
EmailSupport.openSupportEmail()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "exclamationmark.bubble.fill")
|
||||
Text("Feedback or Issues?")
|
||||
}
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 8)
|
||||
.background(Capsule().fill(.thinMaterial))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
private var formattedTimeSaved: String {
|
||||
let formatted = Formatters.formattedDuration(timeSaved, style: .full, fallback: "Time savings coming soon")
|
||||
return formatted
|
||||
@ -250,9 +240,49 @@ private enum Formatters {
|
||||
}
|
||||
}
|
||||
|
||||
private struct FeedbackButton: View {
|
||||
@State private var isClicked: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
openFeedback()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: isClicked ? "checkmark.circle.fill" : "exclamationmark.bubble.fill")
|
||||
.rotationEffect(.degrees(isClicked ? 360 : 0))
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isClicked)
|
||||
|
||||
Text(isClicked ? "Sending" : "Feedback or Issues?")
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isClicked)
|
||||
}
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 8)
|
||||
.background(Capsule().fill(.thinMaterial))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.scaleEffect(isClicked ? 1.1 : 1.0)
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isClicked)
|
||||
}
|
||||
|
||||
private func openFeedback() {
|
||||
EmailSupport.openSupportEmail()
|
||||
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
|
||||
isClicked = true
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
|
||||
isClicked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct CopySystemInfoButton: View {
|
||||
@State private var isCopied: Bool = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
copySystemInfo()
|
||||
@ -260,8 +290,10 @@ private struct CopySystemInfoButton: View {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: isCopied ? "checkmark" : "doc.on.doc")
|
||||
.rotationEffect(.degrees(isCopied ? 360 : 0))
|
||||
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isCopied)
|
||||
|
||||
Text(isCopied ? "Copied!" : "Copy System Info")
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isCopied)
|
||||
}
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.padding(.horizontal, 12)
|
||||
@ -272,16 +304,16 @@ private struct CopySystemInfoButton: View {
|
||||
.scaleEffect(isCopied ? 1.1 : 1.0)
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isCopied)
|
||||
}
|
||||
|
||||
|
||||
private func copySystemInfo() {
|
||||
SystemInfoService.shared.copySystemInfoToClipboard()
|
||||
|
||||
withAnimation {
|
||||
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
|
||||
isCopied = true
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
withAnimation {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
|
||||
isCopied = false
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user