Fix footer button overlap on small screens

This commit is contained in:
Beingpax 2025-11-01 11:49:29 +05:45
parent 45fd4c327b
commit e3ab7d8e80
3 changed files with 88 additions and 57 deletions

View File

@ -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)

View File

@ -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))

View File

@ -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
}
}