Share cards 2.0: dark panorama + privacidad + Story + atribución (build 82) #34

Rediseño de las cards de compartir con el sistema visual de las screenshots
ASO: fondo navy con glows y grid, sparkline de evolución con gradiente
azul→cian y punto final, chip de delta, footer con marca + QR. Aplica a
Portfolio Snapshot, Monthly Check-in y Monthly Summary.

Potencia referral: sheet de opciones previo con preview en vivo
(PortfolioShareOptionsView) — toggle «Solo porcentajes» (sin importes
absolutos, la fricción #1 para compartir) y formato Post/Story 9:16. QR y
link con ?ct=snapshot_share para atribución en App Analytics. Analytics:
share type portfolio_value_percent. L10n ×7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L38583J7AYWCVPevkivscj
This commit is contained in:
alexandrev-tibco
2026-08-06 23:09:59 +02:00
parent 6e6f4c5489
commit 8084bc6a82
12 changed files with 492 additions and 121 deletions
@@ -17,6 +17,7 @@ struct DashboardView: View {
@AppStorage("showForecast") private var showForecast = true
@State private var showingQuickUpdate = false
@State private var showingDataGaps = false
@State private var showingShareOptions = false
/// Signature of the gap set last dismissed, so re-dismissal sticks until the
/// gaps actually change (e.g. the user filled some).
@AppStorage("dismissedDataGapsSignature") private var dismissedGapsSignature = ""
@@ -146,6 +147,17 @@ struct DashboardView: View {
.sheet(isPresented: $viewModel.showingPaywall) {
PaywallView()
}
.sheet(isPresented: $showingShareOptions) {
PortfolioShareOptionsView(
totalValue: viewModel.portfolioSummary.formattedTotalValue,
changeText: "\(viewModel.latestPortfolioChange.formattedAbsolute) (\(viewModel.latestPortfolioChange.formattedPercentage))",
changeLabel: "since last check-in",
yearChange: viewModel.portfolioSummary.formattedYearChange,
sinceInceptionChange: viewModel.portfolioSummary.formattedAllTimeReturn,
sparkline: viewModel.evolutionData.map { NSDecimalNumber(decimal: $0.value).doubleValue },
isPositive: viewModel.latestPortfolioChange.absolute >= 0
)
}
.sheet(isPresented: $showingDataGaps) {
DataGapsSheet(
gaps: viewModel.dataGaps,
@@ -313,13 +325,7 @@ struct DashboardView: View {
isYearPositive: viewModel.isYearChangePositive,
isSinceInceptionPositive: viewModel.portfolioSummary.allTimeReturn >= 0,
onShareTap: {
ShareService.shared.sharePortfolioValue(
totalValue: viewModel.portfolioSummary.formattedTotalValue,
changeText: "\(viewModel.latestPortfolioChange.formattedAbsolute) (\(viewModel.latestPortfolioChange.formattedPercentage))",
changeLabel: "since last check-in",
yearChange: viewModel.portfolioSummary.formattedYearChange,
sinceInceptionChange: viewModel.portfolioSummary.formattedAllTimeReturn
)
showingShareOptions = true
},
sparklineData: viewModel.evolutionData,
balancesHidden: balancePrivacy.balancesHidden