Replace TipKit with custom themed tips modal system
- Add TipModalView with gradient header, themed styling, and spring animations - Create FeelsTipsManager with global toggle, session tracking, and persistence - Define FeelsTip protocol and convert all 7 tips to new system - Add convenience view modifiers (.customizeLayoutTip(), .aiInsightsTip(), etc.) - Remove TipKit dependency from all views - Add Tips Preview debug screen in Settings to test all tip modals - Update documentation for new custom tips system 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -109,38 +109,42 @@ This document covers the new Apple-specific features integrated into Feels, incl
|
||||
|
||||
---
|
||||
|
||||
## 4. TipKit
|
||||
## 4. Custom Tips System
|
||||
|
||||
**File:** `Shared/FeelsTips.swift`
|
||||
**Files:**
|
||||
- `Shared/FeelsTips.swift` (Tips definitions and manager)
|
||||
- `Shared/Views/TipModalView.swift` (Modal UI)
|
||||
|
||||
**What it does:** Shows contextual tips to help users discover features throughout the app.
|
||||
**What it does:** Shows themed modal tips to help users discover features throughout the app. Tips appear as beautiful sheets that match the app's current theme.
|
||||
|
||||
### Available Tips
|
||||
|
||||
| Tip | Location | Trigger |
|
||||
|-----|----------|---------|
|
||||
| CustomizeLayoutTip | Customize screen | First visit |
|
||||
| AIInsightsTip | Insights tab | After 7 days of data |
|
||||
| AIInsightsTip | Insights tab | After 7 moods logged |
|
||||
| SiriShortcutTip | Settings | After 3 mood logs |
|
||||
| HealthKitSyncTip | Settings | After 5 mood logs |
|
||||
| WidgetVotingTip | Day view | After first mood log |
|
||||
| TimeViewTip | Day view header | After 2 days usage |
|
||||
| HealthKitSyncTip | Settings | After viewing settings |
|
||||
| WidgetVotingTip | Day view | After 2 days usage |
|
||||
| TimeViewTip | Day view | First visit |
|
||||
| MoodStreakTip | Day view | When streak >= 3 |
|
||||
|
||||
### How to Test
|
||||
1. Tips appear automatically based on conditions
|
||||
2. To reset tips for testing, add this code temporarily:
|
||||
1. Tips appear automatically based on conditions (one per session)
|
||||
2. To reset tips for testing:
|
||||
```swift
|
||||
try? Tips.resetDatastore()
|
||||
FeelsTipsManager.shared.resetAllTips()
|
||||
```
|
||||
3. To disable tips globally:
|
||||
```swift
|
||||
FeelsTipsManager.shared.tipsEnabled = false
|
||||
```
|
||||
3. Or use the Tips debug menu in Xcode:
|
||||
- Edit Scheme > Run > Arguments
|
||||
- Add: `-com.apple.TipKit.DisplayFrequency weekly`
|
||||
|
||||
### Implementation Details
|
||||
- `TipsManager.shared.configure()` called in `FeelsApp.init()`
|
||||
- Each tip has rules based on `@Parameter` events and conditions
|
||||
- Tips automatically dismiss after user interaction
|
||||
- `FeelsTipsManager.shared.resetSession()` called in `FeelsApp.init()`
|
||||
- Each tip has `isEligible` property based on user activity parameters
|
||||
- Tips show as themed modal sheets with gradient headers
|
||||
- Only one tip shown per app session
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# TipKit Tips Documentation
|
||||
# Custom Tips System Documentation
|
||||
|
||||
This document describes all TipKit tips implemented in the Feels app, including their display conditions and locations.
|
||||
This document describes all tips implemented in the Feels app, including their display conditions and locations.
|
||||
|
||||
## Overview
|
||||
|
||||
Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
- **Display Frequency**: Daily
|
||||
- **Datastore Location**: Application default
|
||||
Tips are displayed as themed modal sheets that match the user's chosen app theme. The system is managed by `FeelsTipsManager` (singleton) and configured with:
|
||||
- **Display Frequency**: One tip per app session
|
||||
- **Global Toggle**: `tipsEnabled` boolean in UserDefaults
|
||||
- **Persistence**: Shown tip IDs stored in UserDefaults
|
||||
|
||||
---
|
||||
|
||||
@@ -15,12 +16,12 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
### 1. CustomizeLayoutTip
|
||||
|
||||
**Title**: "Personalize Your Experience"
|
||||
**Message**: "Tap here to customize mood icons, colors, and layouts."
|
||||
**Icon**: `paintbrush`
|
||||
**Message**: "Customize mood icons, colors, and layouts to make the app truly yours."
|
||||
**Icon**: `paintbrush.fill`
|
||||
|
||||
**Display Conditions**: Always eligible (no rules)
|
||||
|
||||
**Location**: CustomizeContentView (top of the Customize tab in Settings)
|
||||
**Location**: CustomizeContentView (via `.customizeLayoutTip()`)
|
||||
|
||||
---
|
||||
|
||||
@@ -28,14 +29,14 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
|
||||
**Title**: "Discover AI Insights"
|
||||
**Message**: "Get personalized insights about your mood patterns powered by Apple Intelligence."
|
||||
**Icon**: `brain`
|
||||
**Icon**: `brain.head.profile`
|
||||
|
||||
**Display Conditions**:
|
||||
- User has logged at least **7 moods**
|
||||
|
||||
**Parameter**: `hasLoggedMoods: Int` (incremented via `TipsManager.shared.onMoodLogged()`)
|
||||
**Parameter**: `moodLogCount: Int` (incremented via `FeelsTipsManager.shared.onMoodLogged()`)
|
||||
|
||||
**Location**: InsightsView
|
||||
**Location**: InsightsView (via `.aiInsightsTip()`)
|
||||
|
||||
---
|
||||
|
||||
@@ -48,7 +49,7 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
**Display Conditions**:
|
||||
- User has logged at least **3 moods**
|
||||
|
||||
**Parameter**: `moodLogCount: Int` (incremented via `TipsManager.shared.onMoodLogged()`)
|
||||
**Parameter**: `moodLogCount: Int` (incremented via `FeelsTipsManager.shared.onMoodLogged()`)
|
||||
|
||||
**Location**: SettingsContentView (Features section header, via `.siriShortcutTip()`)
|
||||
|
||||
@@ -63,7 +64,7 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
**Display Conditions**:
|
||||
- User has viewed the Settings screen
|
||||
|
||||
**Parameter**: `hasSeenSettings: Bool` (set via `TipsManager.shared.onSettingsViewed()`)
|
||||
**Parameter**: `hasSeenSettings: Bool` (set via `FeelsTipsManager.shared.onSettingsViewed()`)
|
||||
|
||||
**Location**: SettingsContentView (Health Kit toggle, via `.healthKitSyncTip()`)
|
||||
|
||||
@@ -73,14 +74,14 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
|
||||
**Title**: "Vote from Your Home Screen"
|
||||
**Message**: "Add the Mood Vote widget to quickly log your mood without opening the app."
|
||||
**Icon**: `square.grid.2x2`
|
||||
**Icon**: `square.grid.2x2.fill`
|
||||
|
||||
**Display Conditions**:
|
||||
- User has been using the app for at least **2 days**
|
||||
|
||||
**Parameter**: `daysUsingApp: Int` (updated via `TipsManager.shared.updateDaysUsingApp(_:)`)
|
||||
**Parameter**: `daysUsingApp: Int`
|
||||
|
||||
**Location**: DayView
|
||||
**Location**: DayView (via `.widgetVotingTip()`)
|
||||
|
||||
---
|
||||
|
||||
@@ -92,7 +93,7 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
|
||||
**Display Conditions**: Always eligible (no rules)
|
||||
|
||||
**Location**: DayView
|
||||
**Location**: DayView (via `.timeViewTip()`)
|
||||
|
||||
---
|
||||
|
||||
@@ -105,26 +106,29 @@ Tips are managed by `TipsManager` (singleton) and configured with:
|
||||
**Display Conditions**:
|
||||
- User has a current streak of at least **3 days**
|
||||
|
||||
**Parameter**: `currentStreak: Int` (updated via `TipsManager.shared.updateStreak(_:)`)
|
||||
**Parameter**: `currentStreak: Int` (updated via `FeelsTipsManager.shared.updateStreak(_:)`)
|
||||
|
||||
**Location**: DayView
|
||||
**Location**: DayView (via `.moodStreakTip()`)
|
||||
|
||||
---
|
||||
|
||||
## TipsManager API
|
||||
## FeelsTipsManager API
|
||||
|
||||
```swift
|
||||
// Configure tips (call on app launch)
|
||||
TipsManager.shared.configure()
|
||||
// Reset session flag (call on app launch)
|
||||
FeelsTipsManager.shared.resetSession()
|
||||
|
||||
// Reset all tips (for testing)
|
||||
TipsManager.shared.resetAllTips()
|
||||
FeelsTipsManager.shared.resetAllTips()
|
||||
|
||||
// Update parameters
|
||||
TipsManager.shared.onMoodLogged() // Increments mood log count
|
||||
TipsManager.shared.onSettingsViewed() // Marks settings as viewed
|
||||
TipsManager.shared.updateDaysUsingApp(_:) // Updates days using app
|
||||
TipsManager.shared.updateStreak(_:) // Updates current streak
|
||||
FeelsTipsManager.shared.onMoodLogged() // Increments mood log count
|
||||
FeelsTipsManager.shared.onSettingsViewed() // Marks settings as viewed
|
||||
FeelsTipsManager.shared.updateDaysUsingApp(_:) // Updates days using app
|
||||
FeelsTipsManager.shared.updateStreak(_:) // Updates current streak
|
||||
|
||||
// Global toggle
|
||||
FeelsTipsManager.shared.tipsEnabled = true/false
|
||||
```
|
||||
|
||||
---
|
||||
@@ -141,12 +145,26 @@ Tips can be attached to views using these convenience modifiers:
|
||||
.widgetVotingTip()
|
||||
.timeViewTip()
|
||||
.moodStreakTip()
|
||||
|
||||
// Or use the generic modifier with custom gradient colors:
|
||||
.feelsTip(FeelsTips.customizeLayout, gradientColors: [.purple, .blue])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modal Design
|
||||
|
||||
Tips are displayed as themed modal sheets with:
|
||||
- Gradient header (130pt) matching tip-specific colors
|
||||
- SF Symbol icon (44pt, white)
|
||||
- Title and message with theme text color
|
||||
- "Got it" dismiss button with gradient background
|
||||
- Spring animation on appearance
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
- **Definition**: `Shared/FeelsTips.swift`
|
||||
- **Manager**: `TipsManager` class in same file
|
||||
- **Configuration**: Called in `FeelsApp.swift`
|
||||
- **Tips & Manager**: `Shared/FeelsTips.swift`
|
||||
- **Modal View**: `Shared/Views/TipModalView.swift`
|
||||
- **Configuration**: `FeelsTipsManager.shared.resetSession()` called in `FeelsApp.swift`
|
||||
|
||||
Reference in New Issue
Block a user