Commit Graph

37 Commits

Author SHA1 Message Date
alexandrev-tibco 464fbb2bad App de Apple Watch + 3 complicaciones (build 88) #48
App de watchOS de solo consulta: resumen de cartera (total, cambio del
mes y mayores posiciones), objetivos con su progreso y estado del
check-in con la racha. Toda la edición sigue en el iPhone.

Complicaciones (accessoryCircular / Rectangular / Inline / Corner):
valor total de la cartera, progreso del objetivo más cercano a cumplirse
y racha + estado del check-in.

Datos: los App Groups no se comparten entre iOS y watchOS, así que el
reloj no puede leer el store de CoreData que lee el widget de iOS. En su
lugar, WatchSyncService construye un WatchPortfolioSnapshot ligero y lo
envía como application context de WatchConnectivity, enganchado a
CoreDataStack.refreshWidgetData() — el mismo punto que refresca el widget
de iOS, así que cualquier repositorio que toque datos refresca el reloj.
WatchDataStore lo cachea en el App Group del watch (WatchSnapshotCache) y
recarga las timelines; la extensión de complicaciones solo lee ese caché.

Proyecto: dos targets nuevos (watchOS app + widget extension) con grupos
sincronizados de Xcode 16; Shared/ pertenece a la app de watch y se
comparte con la app de iOS y el widget mediante exception sets, el mismo
mecanismo que ya usaba el widget de iOS para el modelo de CoreData.
Scheme compartido PortfolioJournalWatch. Localización propia por target
en los 7 idiomas.

Verificado: compilan los tres targets, la app de watch queda embebida en
Watch/ con la extensión en PlugIns/, y en el par de simuladores
iPhone 17 Pro Max + Apple Watch Series 11 el reloj recibe y muestra el
snapshot enviado por el iPhone. 10 tests nuevos del payload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YcDn5ccuRFV83q7xWWokBT
2026-09-13 22:05:31 +02:00
alexandrev-tibco d88cc59627 Goals por categoría: ámbito de categoría en objetivos (build 88) #46
Un Goal ya podía acotarse por cuenta; ahora también por categoría de
fuente, de modo que se puede fijar "100.000 € en Cash" y que el progreso
cuente solo las fuentes de esa categoría.

- CoreData: relación Goal.category ↔ Category.goals (opcional, Nullify)
- Goal.includes(_:)/relevantSources(from:)/scopeKey centralizan el filtro
  de ámbito (cuenta Y categoría) que antes estaba duplicado en
  GoalsViewModel, AddSourceView y NotificationService
- GoalEditorView: sección "Ámbito" con selector de categoría
- Chip de categoría en la lista de Goals y en la tarjeta del Dashboard
- ETA del Dashboard para goals con ámbito: usa la proyección del propio
  goal en vez de la evolución global de la cartera (que daría "objetivo
  alcanzado" en cuanto la cartera total superara el importe)
- GoalRepository e ImportService deduplican por (nombre, cuenta, categoría)
- Export/Import de goals incluye "category"
- Nuevas claves localizadas en los 7 idiomas

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YcDn5ccuRFV83q7xWWokBT
2026-09-12 09:32:02 +02:00
alexandrev-tibco 3b1f62cc26 Share cards compartibles: brand header grande, fecha, Dark/Light (build 83) #35
- Header de marca arriba, grande (logo BrandMark 48px + nombre title2 rounded),
  nunca se corta en Story (9:16 real 360x640, Post 4:5 360x450).
- Héroe = retorno % (privacy/branding-friendly), valor como apoyo.
- Fecha del último check-in bajo el nombre (share_as_of) para dar contexto a
  quien no conoce la app; DashboardViewModel.shareAsOfDate.
- Tema Dark + Light (ShareCardTheme) con selector en el sheet + preview vivo.
- Sparkline real (viewModel.evolutionData), ya se pasaba.
- CTA de descarga + QR con ct=snapshot_share (atribución App Analytics).
- L10n x7 (as_of, theme_dark/light, cta, hero, metrics).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L38583J7AYWCVPevkivscj
2026-08-07 09:45:10 +02:00
alexandrev-tibco 8084bc6a82 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
2026-08-06 23:09:59 +02:00
alexandrev-tibco d941ecf27b Fix ITMS-90626: quitar 'Siri' de descripciones de App Intents + build 78
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L38583J7AYWCVPevkivscj
2026-08-06 20:11:58 +02:00
alexandrev-tibco 326be6db04 1.6.0 #10 ocultar saldos + Face ID · #11 widget interactivo + Siri
#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
2026-07-25 17:52:09 +02:00
alexandrev-tibco 648c2bd805 1.6.0 #9: auto-backup programado a iCloud Drive/Archivos
El BackupService (export JSON v3 lossless a local + iCloud ubiquity, retención,
restore, premium-gated) ya existía de 1.1.0 pero solo era manual. Añadido lo automático:
- BackupService.runScheduledBackupIfNeeded() (config vía UserDefaults, gate por
  backupsEnabled+premium, respeta frecuencia).
- Disparo en foreground (scenePhase .active) en background queue.
- Frecuencia Off/Diario/Semanal/Mensual + fecha del último backup en Settings
  (SettingsViewModel.autoBackupFrequencyDays + updateAutoBackupFrequency).
- Strings en 7 idiomas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-25 17:31:53 +02:00
alexandrev-tibco 6887d05b01 1.6.0 #6+#7: proyección/ETA de objetivos + insights inteligentes
Goal ETA (#6): GoalProjection engine (CAGR mensual robusto de la evolución +
aportaciones, proyecta mes a mes → fecha estimada, estado ahead/on-track/behind/
unreachable, sparkline). GoalsView muestra 'Projected: <mes>', chip de ritmo con color,
fallback 'Add contributions to reach this'. GoalsViewModel.projection(for:) cacheado.

Insights (#7): InsightsEngine rule-based on-device (sin red/AI). Reglas: allocation vs
target, riesgo de concentración, mejor/peor categoría, diversificación, racha, YTD,
ganancias, forecast, proximidad a milestone. Priorizados por tono, top 4. PortfolioInsight
gana detail + tone. Surface en el Dashboard.

Localización: 19 claves nuevas en los 7 idiomas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-25 12:22:12 +02:00
alexandrev-tibco 84d9240ee6 1.5.0: aviso de huecos + resumen mensual compartible + import con vista previa
Feature 1 — Aviso de huecos: SnapshotGapDetector detecta huecos internos por
source (meses sin datos entre dos snapshots). Banner desechable en Dashboard +
DataGapsSheet para rellenar (abre AddSnapshotView con el mes que falta). Complementa
la interpolación de gráficas: la línea se ve suave pero el usuario sabe que faltan
datos reales.

Feature 2 — Resumen mensual compartible: MonthlySummaryShareView (tarjeta con marca:
valor, variación desde el check-in, racha, mood/rating) renderizada con ImageRenderer
y compartida vía ShareService. Botón en la fila "check-in done" del Dashboard.

Feature 3 — Import con vista previa: ImportPreview + previewImportAsync (dry-run
READ-ONLY, no muta nada) cuenta qué se creará/actualizará usando los mismos checks
de existencia que applyImport. ImportDataView muestra la confirmación antes de
importar. El path real de import (applyImport/importDataAsync) NO se toca.

Localización en los 7 idiomas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-24 09:36:50 +02:00
alexandrev-tibco df9309895a Export/import JSON lossless (v3): goals, monthly contributions, journal, ratings
El JSON v2 perdía Goals, source.monthlyContribution, category.allocationTarget y
todo el Journal (nota/mood/rating/completionTime). Nuevo formato v3:
- source: + monthlyContribution, customFrequencyMonths
- category: + allocationTarget
- top-level: + goals[] (name/targetAmount/targetDate/isActive/account)
- top-level: + journalEntries[] (monthKey/note/mood/rating/completionTime/createdAt)

Import parsea y aplica todo, creando Goals y JournalEntry DIRECTAMENTE en el
contexto de background (GoalRepository es @MainActor → no usable desde el import).
Dedup: goals por nombre+cuenta, journal por monthKey → reimport idempotente.
Retrocompatible con JSON v2. CSV sin cambios.

Guard: si el import trae journal, se salta el marcado de completions derivado de
snapshots (evita JournalEntry duplicados por el desfase de merge viewContext).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-23 20:13:21 +02:00
alexandrev-tibco 047060f126 Fix check-ins no marcados al importar (setCompletionDate en hilo equivocado)
El import corre en un contexto de background, pero MonthlyCheckInStore muta
viewContext (main). Llamar setCompletionDate desde el background era una
violación de concurrencia de Core Data → los JournalEntry.completionTime de los
meses importados no persistían, dejando el "siguiente check-in" anclado a una
entrada vieja (p.ej. May 2025). Envuelto el marcado en viewContext.performAndWait
para ejecutarlo en la cola correcta.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-23 18:59:36 +02:00
alexandrev-tibco d7149aad67 Fix ANR al terminar el import CSV (save + widget refresh por cada fila)
createSnapshot hacía save() por cada snapshot, y save() dispara refreshWidgetData
→ checkpointWAL (performAndWait en el viewContext main) + reloadAllTimelines. Con
213 snapshots eran 213 saves + 213 checkpoints en el main → Application Not
Responding. Añadido modo batch a createSnapshot (no guarda por fila; applyImport
ya guarda el contexto una vez al final) y flag suppressWidgetRefresh en
CoreDataStack para neutralizar el refresh por fila durante el import, con un único
refreshWidgetData al terminar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
2026-07-23 11:38:32 +02:00
alexandrev-tibco 1e6e3ef361 Re-engagement con KPIs reales (goal ETA, YoY, mes, milestone, racha) + rotación (build 64)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZFmGhbWzibhApev3C4554
2026-07-20 19:29:27 +02:00
alexandrev-tibco 4c0d2af564 Calm 2.0 Fase 4: TipKit para descubrimiento + aha post-onboarding (build 55)
TipKit (Tips.configure con frecuencia diaria):
- QuickUpdateTip: popover en el '+' del Home — descubre Quick Update
- ScreenshotOCRTip: inline en Quick Update — descubre el OCR por captura
- SiriTip: en el cierre del check-in guiado — descubre el intent de voz
- ChartShareTip: popover en el compartir de Charts
- Se invalidan al ejecutar la acción; máx 1-2 impresiones cada uno
- Strings ×7 idiomas

Onboarding:
- Al completar el onboarding sin datos, se abre Add Source directamente
  (notificación .openAddFirstSource) — el usuario nuevo no aterriza en un
  dashboard vacío

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
2026-07-10 09:31:03 +02:00
alexandrev-tibco 0e0aec6bb9 Charts: fix selección iPad/charts bloqueadas, filtro Rolling 12M, KPIs específicos y botón compartir
Bugs:
- Charts bloqueadas (intermitente) y tiles que no responden: el observer Combine
  registraba el estado como actualizado ANTES de llamar a updateChartData, cuyo
  guard de reentrada descartaba la llamada en silencio — reintentarlo era no-op
  permanente. Ahora el bookkeeping vive dentro de updateChartData, las llamadas
  reentrantes se colean en vez de descartarse, y selectChart computa en síncrono
  (pulsar un tile es determinista y re-pulsar actúa de retry).
- Paywall sheet: movida del Group que cambia con el size class a cada layout —
  en NavigationSplitView no llegaba a presentarse (tiles premium 'no hacían nada').
- Rolling 12M: el filtro de periodo no filtraba — el cálculo necesita histórico
  completo para el lookback, pero ahora la salida respeta selectedTimeRange.

KPIs:
- El header de iPad muestra KPIs específicos del chart activo (los mismos que su
  stats row) en vez de las 5 métricas de cartera fijas; las stats rows dentro de
  las cards se ocultan en regular width para no duplicar (YoY opta por quedarse
  al no tener equivalente en el header).

Compartir:
- Botón de compartir por chart (toolbar iPad + navbar iPhone): renderiza la
  gráfica en una card con branding (BrandMark, KPIs, tagline, QR al App Store
  con ct=chart_share) vía ImageRenderer y abre el share sheet con imagen + link.
- drawingGroup() se desactiva durante el export (ImageRenderer no rasteriza
  capas Metal — salían en blanco). Strings nuevas en 7 idiomas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
2026-07-08 16:32:42 +02:00
alexandrev-tibco a830a5f40c Intent paramétrico Log Value + frases de Siri localizadas en 7 idiomas
LogSnapshotIntent: 'Registra el saldo de Indexa' por voz o desde Atajos, sin
abrir la app (openAppWhenRun=false). SourceEntity expone las fuentes a
Siri/Atajos desde el mirror del App Group; el valor entra por la misma cola
que el Share Extension y se materializa como Snapshot al instante (el intent
corre en el proceso de la app). Diálogo de confirmación con formato de divisa
de la fuente.

AppShortcuts.xcstrings: las 7 frases de los 3 shortcuts localizadas a
es-ES/de/fr/it/ja/pt-BR — Siri en español ya funciona. Strings de UI de
Atajos (títulos, parámetros, diálogos) añadidas a los 7 Localizable.strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
2026-07-08 16:11:18 +02:00
alexandrev-tibco 19b1408c55 1.4.2 (build 48): OCR de screenshots en Share Extension
El extension acepta ahora imágenes además de texto: al compartir un screenshot
del banco/broker, Vision reconoce el texto EN EL DISPOSITIVO (la imagen nunca
sale del teléfono), extrae los importes candidatos y rellena el más prominente
visualmente (en las UIs bancarias el balance es el número más grande). El resto
de candidatos se ofrecen como chips de un toque. Percentajes y años se filtran.

- ExtImageAmountScanner.swift: VNRecognizeTextRequest + ranking por altura de
  bounding box + dedup por valor
- ShareViewController: carga UIImage/URL/Data del provider, estado de escaneo,
  sección de candidatos
- Info.plist: NSExtensionActivationSupportsImageWithMaxCount=1
- Strings nuevas en los 7 idiomas (ext_scanning_image, ext_detected_amounts,
  ext_ocr_no_amounts)
- PredictionEngine: troceado ternario que agotaba el type-checker en Debug
- Bump a 1.4.2 build 48

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
2026-07-08 15:59:54 +02:00
alexandrev-tibco 197bddcd7e Growth y monetización: fixes críticos, paywall multi-plan, teasers y App Intents
CRÍTICO — enlaces del App Store rotos (adquisición viral = 0):
- GoalShareService.appStoreURL apuntaba a id6744983373 (404): el QR de TODAS las
  imágenes compartidas llevaba a una página muerta. Corregido a id6757678318.
- SettingsView enlazaba id6741412965 (una novela romántica). Ahora usa la constante.

Monetización:
- IAPService multi-producto: sub anual (com.portfoliojournal.premium.annual, con
  intro offer/trial) + lifetime como ancla. isPremium acepta cualquiera de los dos.
  Paywall con selector de plan (anual por defecto si existe; degrada a solo lifetime
  mientras el producto no esté creado en App Store Connect).
- paywallBenefits: añadidos Multiple Accounts y Family Sharing (diferenciadores).
- Teaser de historia bloqueada en Charts: "N meses más con Premium" en vez de
  truncar en silencio (FreemiumValidator.hiddenHistoryMonths + banner).
- Trigger de paywall de backups ahora instrumentado (logPaywallShown "backups").

Retención / adquisición:
- Rating velocity: prompt tras 2 check-ins y 30 días (antes 3 + 90 — en una app
  mensual eso era >3 meses sin poder pedir la primera review con ~0 ratings).
- Sample data ON por defecto en onboarding (skip ya no aterriza en dashboard vacío).
- Notificación de protección de streak el día 25 si el check-in del mes está pendiente.
- App Intents + AppShortcutsProvider: "Update my portfolio" / "Check my portfolio"
  vía Siri/Shortcuts/Spotlight.
- Instrumentación: onboarding_step/onboarding_skipped y content_shared (viral loop).
- ScreenshotMode (--screenshots) para capturas de marketing automatizadas.

ASO:
- 6 locales nuevos en metadata (en-GB, en-CA, en-AU, es-MX, fr-CA, pt-PT) reusando
  traducciones — 6 campos de keywords extra; pt-PT adaptado (reforma).
- Categoría secundaria: PRODUCTIVITY.
- 17 strings nuevas localizadas en los 7 idiomas.

Pendiente manual: crear la sub anual en App Store Connect (grupo de suscripción +
intro offer 7 días) con el id exacto com.portfoliojournal.premium.annual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
2026-07-02 22:12:32 +02:00
alexandrev-tibco 7a18dd8360 1.4.1 (build 42): contribución por snapshot, fixes Period vs Period y navegación iPad
- 151: contribución editable por snapshot en cualquier modo (quitado gate detailed)
  con diálogo de propagación al editar: solo este / adelante / atrás / todos
  (SnapshotRepository.propagateContribution, SnapshotFormViewModel.contributionChanged)
- 148: Period vs Period mostraba mal el último mes del period B — la agrupación usaba
  chartMonth(for:) que aplicaba el grace-period del check-in a snapshots históricos;
  ahora agrupa por mes calendario crudo
- 152: iPad Sources no saltaba entre fuentes — añadido .id() al SourceDetailView para
  recrear el StateObject al cambiar de selección
- 146/147: Year vs Year con forecast del año en curso (asterisco de estimado) y KPIs
  arriba / detalle debajo
- 145: card de contribución mensual en SourceDetailView
- Nuevas claves snapshot_contribution_propagate_* en los 7 idiomas

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
2026-06-30 17:01:50 +02:00
alexandrev-tibco 0b01d6f563 Fix iCloud sync: chart and next check-in date stale on secondary device (build 32)
- completedMonthKeys: remove UserDefaults-based MonthlyCheckInStore check (not synced via iCloud)
- MonthlyCheckInCard.effectiveLastCheckInDate: use max() instead of ?? to prefer newer synced snapshot date
- MonthlyCheckInView.lastCompletionDate: use max() of local + latest CoreData snapshot via @FetchRequest
- FirebaseService.logPaywallShown: fix parameter key to paywall_trigger (matches GA4 custom dimension)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 08:42:22 +02:00
alexandrev-tibco b48a47ce10 Release 1.4.0 (build 31): retención, quick update, streak, what's new
- 1A: Notificación mensual de resumen de portfolio (día 5 de cada mes)
- 1B: Badge de racha mensual en Dashboard (streak counter)
- 1C: Empty states mejorados en Goals y Journal con CTAs claros
- 2A: Quick Update sheet — actualiza todas las fuentes desde una pantalla
- 2B: Notificación batch update redirige al Quick Update sheet
- 2C: Widget deep link portfoliojournal://quickupdate abre Quick Update
- 3A: App Store version check banner en Settings
- 3C: What's New sheet en primer launch de versión nueva
- Localización completa en 7 idiomas para todas las nuevas strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:09:36 +02:00
alexandrev-tibco 94ed4d17eb Release 1.3.0: onboarding UX, charts paywall banner, re-engagement notifications, Crashlytics
- Crashlytics: add FirebaseCrashlytics framework + dSYM upload build phase
- Onboarding: useSampleData off by default, Add First Investment as primary CTA
- AddSourceView: contextual placeholder and footer explaining what a source is
- Charts: CompactPaywallBanner visible to free users on every visit
- Notifications: re-engagement (7d) and monthly check-in local notifications
- Paywall: decorative chart preview replacing static crown icon
- Localization: all new strings in en + es-ES
2026-05-01 09:26:37 +02:00
alexandrev-tibco 10f6d0ca20 Release 1.2.1: iCloud sync improvements + ASO multilingual metadata
iCloud sync:
- Force viewContext.refreshAllObjects() on remote change notifications so
  data from other devices is picked up immediately without app restart
- Call refreshFromCloudKit() on foreground to merge any changes made while
  the device was inactive
- Wait up to 10s for initial CloudKit sync on launch before showing onboarding
  (shows "Checking iCloud..." during the wait)
- New OnboardingICloudCheckView: shown on fresh installs with iCloud available,
  lets user restore from iCloud before starting onboarding from scratch

Localization:
- Added de, fr, it, ja, pt-BR lproj folders
- New iCloud onboarding strings in en + es-ES (+ button literals)

ASO metadata (fastlane):
- Updated en-US: new subtitle, keywords, description (fixed "no cloud sync"
  claim), promotional text, release notes
- Added full metadata for es-ES, de-DE, fr-FR, it, ja, pt-BR (63 files total)
- All keyword fields validated ≤100 Unicode chars

Infrastructure:
- Gemfile + Gemfile.lock for fastlane
- Scripts/archive_and_upload_appstore.sh for CI/CD

Version: 1.2.1 (build 7)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:54:35 +01:00
alexandrev-tibco 7cb5f92cf4 Remove Add Transaction feature and clean all related code
Deleted files:
- AddTransactionView.swift
- TransactionRepository.swift
- Transaction+CoreDataClass.swift

Cleaned files:
- SourceDetailViewModel: removed transactions published property,
  showingAddTransaction flag, transactionRepository dependency,
  addTransaction() and deleteTransaction() methods
- SourceDetailView: removed transactionsSection, TransactionRow struct,
  Add Transaction button from quickActions, sheet presentation
- InvestmentSource+CoreDataClass: removed transactions NSManaged property,
  transactionsArray, totalInvested, totalDividends, totalFees computed
  properties, and all transaction Core Data accessors
- SettingsViewModel: removed "Transaction" from resetAllData entity list
- SampleDataService: removed transactionRepository and sample transaction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 23:04:03 +01:00
alexandrev-tibco 472342fa67 Ensure full Spanish localization for all 1.2.0 strings
- OnboardingView: wrap page titles/descriptions with String(localized:)
  using symbolic keys so SwiftUI can look them up correctly
- IAPService.paywallBenefits: change from static let to static var,
  wrap all strings with String(localized:) using symbolic keys
- en.lproj: add symbolic keys for new onboarding pages and paywall benefits
- es-ES.lproj: add Spanish translations for all new keys (onboarding pages,
  paywall benefits, batch update, contribution fields) plus pre-existing
  gaps (Monthly Highlights, Best/Worst Performer, Best Contributor)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 22:41:40 +01:00
alexandrev-tibco edb04efc86 Redesign onboarding and paywall for 1.2.0
- Onboarding: replace feature-focused slides with outcome-focused copy
  ("Know exactly where you stand", "5 minutes a month is enough", etc.)
- Paywall: non-scrollable layout, reduce from 8 to 4 key benefits,
  remove transactional language (Unlock/Upgrade/Premium),
  new CTA "Get Full Access", floating close button
- Add IAPService.paywallBenefits for the condensed 4-item paywall list
- Update CompactPaywallBanner and PremiumLockOverlay copy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 12:51:13 +01:00
alexandrev-tibco 4761e2e5c8 1.1.0 feature work: Monthly Check-in, Charts, Goals, Share, Reviews
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:37:11 +01:00
alexandrev-tibco 2437dd647f Add tests for 1.1.0 features 2026-02-01 14:40:10 +01:00
alexandrev-tibco 0ddee49dd5 Tune app review prompt cadence 2026-02-01 11:35:36 +01:00
alexandrev-tibco edfd7a2a56 Add monthly check-in sharing 2026-02-01 11:32:20 +01:00
alexandrev-tibco b5ba6c47a8 Add premium backups with retention and iCloud support 2026-02-01 11:23:41 +01:00
alexandrev-tibco f97f8026bc Improve goal sharing experience 2026-02-01 11:14:48 +01:00
alexandrev-tibco e328767c4a Base fixes and test harness 2026-02-01 11:12:57 +01:00
alexandrev f5b13ec924 Changes for admob 2026-01-30 22:55:11 +01:00
alexandrev b03d35194f Primera build enviada 2026-01-19 14:40:43 +01:00
alexandrev c6be398e5a Update version 2026-01-16 11:28:26 +01:00
alexandrev-tibco 7988257399 initial version 2026-01-15 09:24:06 +01:00