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
This commit is contained in:
@@ -176,6 +176,62 @@ extension Notification.Name {
|
||||
static let didResetData = Notification.Name("didResetData")
|
||||
}
|
||||
|
||||
// MARK: - Re-engagement Notifications
|
||||
|
||||
extension NotificationService {
|
||||
/// Schedules a re-engagement notification 7 days from now.
|
||||
/// Call this every time the app becomes active to reset the timer.
|
||||
func scheduleReEngagementNotification() {
|
||||
guard isAuthorized else { return }
|
||||
|
||||
center.removePendingNotificationRequests(withIdentifiers: ["re_engagement"])
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = String(localized: "reengagement_title")
|
||||
content.body = String(localized: "reengagement_body")
|
||||
content.sound = .default
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 7 * 24 * 3600, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: "re_engagement", content: content, trigger: trigger)
|
||||
|
||||
center.add(request) { error in
|
||||
if let error = error {
|
||||
print("Re-engagement notification error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedules a monthly check-in notification on the 1st of each month at 9am.
|
||||
/// Only schedules if not already pending.
|
||||
func scheduleMonthlyCheckIn() {
|
||||
guard isAuthorized else { return }
|
||||
|
||||
let identifier = "monthly_checkin"
|
||||
center.getPendingNotificationRequests { [weak self] requests in
|
||||
guard let self, !requests.contains(where: { $0.identifier == identifier }) else { return }
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = String(localized: "monthly_checkin_notification_title")
|
||||
content.body = String(localized: "monthly_checkin_notification_body")
|
||||
content.sound = .default
|
||||
|
||||
var components = DateComponents()
|
||||
components.day = 1
|
||||
components.hour = 9
|
||||
components.minute = 0
|
||||
|
||||
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
|
||||
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
|
||||
|
||||
self.center.add(request) { error in
|
||||
if let error = error {
|
||||
print("Monthly check-in notification error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Background Refresh
|
||||
|
||||
extension NotificationService {
|
||||
|
||||
Reference in New Issue
Block a user