#10 Ocultar saldos: BalancePrivacyManager (balancesHidden + requireBiometricToReveal), modifier .hiddenBalance() (blur + máscara de bullets, ancho estable), botón ojo en Dashboard, revelar con Face ID vía AppLockService, sección Privacidad en Settings. Aplicado a total portfolio, forecast, sources, goals, monthly summary. #11 Siri/App Intents: NetWorthIntent ('¿cuál es mi patrimonio?' → 'Tu patrimonio es X'), StreakIntent, 5 AppShortcuts con frases. Widget interactivo iOS 17: botón Refresh (Button(intent:) → reloadAllTimelines, in-process) en medium/large, gated #available(iOS 17). Reader read-only del AppGroup. Dialogs Siri refactorizados a String(localized:). Localización: strings de #10 y de Siri en los 7 idiomas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import AppIntents
|
||||
|
||||
private let appGroupIdentifier = "group.com.alexandrevazquez.portfoliojournal"
|
||||
private let storeFileName = "PortfolioJournal.sqlite"
|
||||
@@ -753,9 +754,13 @@ struct MediumWidgetView: View {
|
||||
HStack(spacing: 16) {
|
||||
// Left side - Total value + engagement metrics
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Portfolio")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
HStack(spacing: 6) {
|
||||
Text("Portfolio")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer(minLength: 0)
|
||||
WidgetRefreshButton()
|
||||
}
|
||||
|
||||
Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode))
|
||||
.font(.title.weight(.bold))
|
||||
@@ -884,6 +889,7 @@ struct LargeWidgetView: View {
|
||||
VStack(alignment: .trailing, spacing: 6) {
|
||||
if entry.streak >= 1 { StreakPill(streak: entry.streak) }
|
||||
CheckInPill(date: entry.nextCheckInDate)
|
||||
WidgetRefreshButton()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,6 +1229,26 @@ struct CombinedCategoryChartView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interactive Refresh Button (iOS 17+)
|
||||
|
||||
/// A small refresh control that reloads the widget timeline in-process via an
|
||||
/// AppIntent. Only rendered on iOS 17+, where `Button(intent:)` is available;
|
||||
/// on iOS 16 it collapses to nothing so the widget still builds and works with
|
||||
/// the existing deep links.
|
||||
struct WidgetRefreshButton: View {
|
||||
var body: some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
Button(intent: RefreshWidgetIntent()) {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
.font(.caption2.weight(.semibold))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.invalidatableContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Widget Configuration
|
||||
|
||||
struct InvestmentWidget: Widget {
|
||||
|
||||
Reference in New Issue
Block a user