- "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
- Tapping the widget now deep-links via mealmood://today back to the current
week (URL scheme registered, onOpenURL handler in Home)
- New Lock Screen widget families: accessoryRectangular (weekday + both
meals) and accessoryInline (next relevant meal)
- One-time dismissable widget promo card on Home from the 2nd session,
with widget_promo_shown/dismissed analytics
- 2 new strings × 6 languages
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtWT52pAE91D7mbDda1cAM
- New "Week Ready" onboarding step: auto-fills the first week inline right
after dish creation, then pitches the Sunday reminder with context before
requesting OS notification permission (no more cold prompt on Home)
- Notifications section in Settings: weekly reminder toggle, deep-link to
system settings when permission is denied
- NotificationService: remindersEnabled preference, requestPermission(),
authorizationStatus(), cancelAllReminders()
- Post-onboarding premium prompt deferred to 2nd session (paywall was just
shown as the final onboarding step — 2s-later alert was prompt fatigue)
- Back navigation blocked after onboarding commit to avoid duplicate dishes
- 10 new strings × 6 languages
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtWT52pAE91D7mbDda1cAM
- dish_added now carries a `source` param; onboarding-created dishes fire
it with source="onboarding" (previously untracked, undercounting activation)
- onboarding_completed now reports dish_count
- premium_purchased tagged with StoreKit environment (production/sandbox/xcode)
so test purchases can be excluded from conversion metrics
- add .gitignore to keep signing secrets (.p12/.cer/.mobileprovision) and
build artifacts out of the repo
- document branch↔MARKETING_VERSION naming convention
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UtWT52pAE91D7mbDda1cAM
Land new users on a ready, auto-planned week (with confetti) instead of an
empty one, to improve activation. Only fires when the user added dishes;
otherwise keeps the current empty-week + auto-assign prompt behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Build 50 uploaded to TestFlight. Also migrates Fastfile from
Apple ID + app-specific-password to App Store Connect API key
(via pass) to avoid session expiry on future uploads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A) Implicit: HomeViewModel tracks auto-assigned slot IDs after each
autocomplete run. When the user manually replaces one of those dishes,
the rejected dish ID is recorded in FeedbackStore (UserDefaults).
AutocompleteEngine applies a 25% penalty per rejection (floor 0.30).
B) Explicit: WeekPlan gains userRating (0/1/-1). Past weeks with dishes
show a compact 👍/👎 row (Premium only). Liked weeks boost their
dishes (+0.20 each, max +0.50); disliked weeks penalise them
(-0.25 each, max -0.50). Signal is intentionally modest so the
period cycle score remains dominant.
Also adds Stats + week-rating localization strings to all 6 languages
(were missing from the previous commit).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace flat recency penalty with cycle detection. For each dish with
≥2 appearances in history, compute the median interval between uses and
score by how well weeksSinceLast aligns with that period:
ratio < 0.5 → 0.10 (too soon)
ratio ~1.0 → 3.00 (right on schedule, boost)
ratio > 2.5 → 1.00 (pattern may have changed, neutral)
Expand history window from 4 to 12 weeks so monthly cycles (period=4)
have enough data points to be detected reliably.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AutocompleteEngine: MRV heuristic picks the most-constrained slot first
at each step; historical scoring deprioritises dishes used in recent
weeks (decay: 0.20 last week → 0.90 four+ weeks ago)
- HomeViewModel: passes last 4 weeks as recentPlans to autocomplete
- StatsView (Premium): streak, weeks planned, completion rate, top 8
dishes with bar charts, top 6 tags with colour bars — 6 languages
- StoreManager: update product ID to approved bundle-ID convention
- Version bump to 1.1.3 / build 48
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove legacy CONSUMABLE fallback from monthlyProduct — when the
subscription fails to load, the UI now shows the retry state instead
of silently offering the legacy one-time product for purchase.
Legacy purchasers retain access via hasActiveSubscription() as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch premium to AutoRenewable subscription (com.mealmood.premium.monthly.sub);
legacy one-time purchasers retain lifetime access via StoreManager fallback
- Add ITSAppUsesNonExemptEncryption=false to Info.plist (no custom encryption)
- Update storekit config with both legacy NonConsumable and new subscription
- Bump version 1.1.1 build 46; update fastlane metadata and release notes (6 langs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add = false/true defaults to all non-optional Bool properties so
SwiftData's lightweight migration knows what value to assign existing
rows. Replace try! ModelContainer with crash-safe do-catch that resets
the store on migration failure instead of crashing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dish.isPriority: mark dishes as priority; auto-assign gives them 2.5x weight
- Tag.dayRestriction: restrict tags to weekdays or weekend only
- Auto-assign CTA banner shown when there are empty slots (was hidden in context menu)
- Tap on filled slot opens picker to replace dish directly (no need to remove first)
- Export callout visible as soon as ≥1 slot is filled (not only when week is complete)
- WeekPlanShareView shows localized "TBD" / "Eating out" for empty/eating-out slots
- Second Sunday 17:00 push notification to plan next week
- DishDrawer sorts: priority first, then by historical usage, then alphabetically
- Search in SlotDishPickerSheet already shipped in 1.0.5 (verified ✓)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Eating out: tap any empty slot → "Mark as eating out"; shows teal indicator,
skipped by auto-assign, counts as complete, tap again to unmark
- Rule violations panel: access via wand long-press context menu when conflicts exist;
shows all isRuleOverridden slots with Fix (clear) or Ignore (acknowledge) actions
- Firebase Crashlytics integrated: CrashlyticsService + dSYM upload build phase,
isPremium property tracked per session
- PremiumSyncService: extracted premium state machine, StoreKit Transaction.updates
listener, isPremium no longer synced via iCloud to avoid stale state
- StoreManager: analytics on purchase/restore, bundle ID fallback for product ID lookup
- Search bar contrast bug fixed: TextField now has explicit foreground color for dark mode
- WelcomeStepView: redesigned onboarding welcome screen with week preview
- Version bump: 1.0.5 build 22
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AutocompleteEngine: no-repeat rule now applies to all dishes (not just tagless ones); fallback still allows repeats when no alternative exists
- CalendarService: update existing events instead of delete+create to prevent duplicates; fix weekStartDate using plan date instead of currentWeekStart
- DishFormView: form stays open after saving in create mode for quick multi-dish entry; Done button to close; saved feedback banner
- OnboardingViewModel/FirstDishesStepView: dishes are now optional during onboarding; Finish button always enabled
- AnalyticsService: new events — onboarding_step_viewed(step) and auto_assign_used(filled, unfilled)
- Localization: dish_done and first_dishes_optional_hint added in 6 languages; release notes updated in all 6 languages
- Version bump: 1.0.3 → 1.0.4 (build 19)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add localisations for German, French, Italian, and Brazilian Portuguese
- Redesign WeekPlanShareView with richer layout and sharing options
- Improve HomeView with various UI enhancements
- Update AppSettings and Tag models for new features
- Minor iCloud sync and Settings fixes
- Add archive & upload script for App Store submissions
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>