cbe0ca1326
- "Redeem promo code" button in PremiumView via offerCodeRedemption (hidden on Mac where the sheet is unavailable); entitlement re-checked on success — unblocks influencer/blog promo campaigns - System share sheet now includes a localized text with the mealmood.app link next to the exported image (viral loop no longer QR-only) - 2 new strings × 6 languages Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UtWT52pAE91D7mbDda1cAM
141 lines
5.2 KiB
Swift
141 lines
5.2 KiB
Swift
import SwiftUI
|
|
import UIKit
|
|
|
|
struct SocialShareSheet: View {
|
|
let imageURL: URL
|
|
@State private var showSystemShare = false
|
|
@State private var showCopiedToast = false
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
private var image: UIImage? { UIImage(contentsOfFile: imageURL.path) }
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
VStack(spacing: 24) {
|
|
if let img = image {
|
|
Image(uiImage: img)
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(maxHeight: 220)
|
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
|
.shadow(color: .black.opacity(0.12), radius: 14, x: 0, y: 6)
|
|
}
|
|
|
|
VStack(spacing: 10) {
|
|
if canOpenInstagram {
|
|
shareOptionRow(
|
|
title: "Instagram Stories",
|
|
subtitle: String(localized: "share_instagram_subtitle"),
|
|
icon: "camera.fill",
|
|
color: Color(red: 0.80, green: 0.18, blue: 0.75),
|
|
action: shareToInstagramStories
|
|
)
|
|
}
|
|
|
|
shareOptionRow(
|
|
title: String(localized: "share_copy_image"),
|
|
subtitle: String(localized: "share_copy_subtitle"),
|
|
icon: "doc.on.doc.fill",
|
|
color: .mealMoodCoral,
|
|
action: copyImage
|
|
)
|
|
|
|
shareOptionRow(
|
|
title: String(localized: "share_more_options"),
|
|
subtitle: String(localized: "share_more_subtitle"),
|
|
icon: "square.and.arrow.up.fill",
|
|
color: Color(hex: "#4A6CF7"),
|
|
action: { showSystemShare = true }
|
|
)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(20)
|
|
.navigationTitle(String(localized: "share_export_sheet_title"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button(String(localized: "dish_cancel")) { dismiss() }
|
|
}
|
|
}
|
|
}
|
|
.toast(isShowing: $showCopiedToast, message: String(localized: "share_copied"))
|
|
.sheet(isPresented: $showSystemShare) {
|
|
// Ship the link as text alongside the image so the viral loop isn't
|
|
// limited to the QR baked into the rendered export.
|
|
SystemShareSheet(activityItems: [imageURL, String(localized: "share_link_text")])
|
|
}
|
|
}
|
|
|
|
// MARK: - Row view
|
|
|
|
private func shareOptionRow(title: String, subtitle: String, icon: String, color: Color, action: @escaping () -> Void) -> some View {
|
|
Button(action: action) {
|
|
HStack(spacing: 14) {
|
|
Image(systemName: icon)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
.foregroundColor(.white)
|
|
.frame(width: 44, height: 44)
|
|
.background(color)
|
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(title)
|
|
.font(.mealMoodBodyBold)
|
|
.foregroundColor(.mealMoodTextPrimary)
|
|
Text(subtitle)
|
|
.font(.mealMoodCaption)
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Image(systemName: "chevron.right")
|
|
.font(.system(size: 12, weight: .semibold))
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
}
|
|
.padding(12)
|
|
.background(Color.mealMoodSurface)
|
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
private var canOpenInstagram: Bool {
|
|
guard let url = URL(string: "instagram-stories://share") else { return false }
|
|
return UIApplication.shared.canOpenURL(url)
|
|
}
|
|
|
|
private func shareToInstagramStories() {
|
|
guard let img = image,
|
|
let data = img.pngData(),
|
|
let url = URL(string: "instagram-stories://share?source_application=com.alexandrevazquez.mealmood")
|
|
else { return }
|
|
UIPasteboard.general.setData(data, forPasteboardType: "com.instagram.sharedSticker.backgroundImage")
|
|
UIApplication.shared.open(url)
|
|
dismiss()
|
|
}
|
|
|
|
private func copyImage() {
|
|
guard let img = image else { return }
|
|
UIPasteboard.general.image = img
|
|
showCopiedToast = true
|
|
HapticManager.shared.notification(type: .success)
|
|
}
|
|
}
|
|
|
|
// MARK: - System share wrapper (private)
|
|
|
|
private struct SystemShareSheet: UIViewControllerRepresentable {
|
|
let activityItems: [Any]
|
|
|
|
func makeUIViewController(context: Context) -> UIActivityViewController {
|
|
UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
|
|
}
|
|
|
|
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
|
|
}
|