This commit is contained in:
Trey t
2022-07-18 20:14:48 -05:00
parent 044e0bb027
commit 53334e5fb8
10 changed files with 134 additions and 10 deletions

View File

@@ -16,7 +16,8 @@ struct FeelsApp: App {
let persistenceController = PersistenceController.shared let persistenceController = PersistenceController.shared
@StateObject var iapManager = IAPManager() @StateObject var iapManager = IAPManager()
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
init() { init() {
BGTaskScheduler.shared.cancelAllTaskRequests() BGTaskScheduler.shared.cancelAllTaskRequests()
BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in

View File

@@ -20,7 +20,8 @@ public enum StoreError: Error {
class IAPManager: ObservableObject { class IAPManager: ObservableObject {
@Published private(set) var showIAP = false @Published private(set) var showIAP = false
@Published private(set) var subscriptions = [Product: (status: [Product.SubscriptionInfo.Status], renewalInfo: RenewalInfo)?]() @Published private(set) var subscriptions = [Product: (status: [Product.SubscriptionInfo.Status], renewalInfo: RenewalInfo)?]()
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
public var sortedSubscriptionKeysByPriceOptions: [Product] { public var sortedSubscriptionKeysByPriceOptions: [Product] {
subscriptions.keys.sorted(by: { subscriptions.keys.sorted(by: {
$0.price < $1.price $0.price < $1.price
@@ -74,23 +75,52 @@ class IAPManager: ObservableObject {
var updateListenerTask: Task<Void, Error>? = nil var updateListenerTask: Task<Void, Error>? = nil
public var daysLeftBeforeIAP: Int {
let daysSinceInstall = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: firstLaunchDate, to: Date())
if let days = daysSinceInstall.day {
return 30 - days
}
return 0
}
public var expireDate: Date? {
Calendar.current.date(byAdding: .day, value: 30, to: firstLaunchDate) ?? nil
}
private let iapIdentifiers = Set([ private let iapIdentifiers = Set([
"com.88oakapps.ifeel.IAP.subscription.weekly", "com.88oakapps.ifeel.IAP.subscription.weekly",
"com.88oakapps.ifeel.IAP.subscription.monthly", "com.88oakapps.ifeel.IAP.subscription.monthly",
"com.88oakapps.ifeel.IAP.subscription.yearly" "com.88oakapps.ifeel.IAP.subscription.yearly"
]) ])
var expireOnTimer: Timer?
init() { init() {
//Start a transaction listener as close to app launch as possible so you don't miss any transactions. //Start a transaction listener as close to app launch as possible so you don't miss any transactions.
updateListenerTask = listenForTransactions() updateListenerTask = listenForTransactions()
refresh() refresh()
setUpdateTimer()
} }
deinit { deinit {
updateListenerTask?.cancel() updateListenerTask?.cancel()
} }
func setUpdateTimer() {
if let expireDate = expireDate {
expireOnTimer = Timer.init(fire: expireDate, interval: 0, repeats: false, block: { _ in
self.decideShowIAP()
})
RunLoop.main.add(expireOnTimer!, forMode: .common)
} else {
if let expireOnTimer = expireOnTimer {
expireOnTimer.invalidate()
}
}
}
func refresh() { func refresh() {
Task { Task {
//During store initialization, request products from the App Store. //During store initialization, request products from the App Store.
@@ -104,16 +134,25 @@ class IAPManager: ObservableObject {
} }
func decideShowIAP() { func decideShowIAP() {
guard !subscriptions.isEmpty else { // if we have a sub in the subscriptions dict
return // then we dont need to show
}
var tmpShowIAP = true
for (_, value) in self.subscriptions { for (_, value) in self.subscriptions {
if value != nil { if value != nil {
tmpShowIAP = false DispatchQueue.main.async {
self.showIAP = false
}
return return
} }
} }
var tmpShowIAP = true
// if its passed 30 days with no sub
if daysLeftBeforeIAP <= 0 {
tmpShowIAP = true
} else {
tmpShowIAP = false
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.showIAP = tmpShowIAP self.showIAP = tmpShowIAP
} }

View File

@@ -25,6 +25,7 @@ class UserDefaultsStore {
case showNSFW case showNSFW
case shape case shape
case daysFilter case daysFilter
case firstLaunchDate
case contentViewCurrentSelectedHeaderViewBackDays case contentViewCurrentSelectedHeaderViewBackDays
case contentViewHeaderTag case contentViewHeaderTag
@@ -53,6 +54,7 @@ class UserDefaultsStore {
} }
static func moodMoodImagable() -> MoodImagable.Type { static func moodMoodImagable() -> MoodImagable.Type {
return MoodImages.Emoji.moodImages
if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodImages.rawValue) as? Int, if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodImages.rawValue) as? Int,
let model = MoodImages.init(rawValue: data) { let model = MoodImages.init(rawValue: data) {
return model.moodImages return model.moodImages

View File

@@ -40,6 +40,7 @@ struct AddMoodHeaderView: View {
}, label: { }, label: {
mood.icon mood.icon
.resizable() .resizable()
.aspectRatio(contentMode: .fit)
.frame(width: CGFloat(50), height: CGFloat(50), alignment: .center) .frame(width: CGFloat(50), height: CGFloat(50), alignment: .center)
.foregroundColor(moodTint.color(forMood: mood)) .foregroundColor(moodTint.color(forMood: mood))
}) })

View File

@@ -88,8 +88,12 @@ struct DayView: View {
} }
} }
} }
.onAppear{
iapManager.decideShowIAP()
}
} }
// MARK: Views // MARK: Views
public var mainView: some View { public var mainView: some View {
VStack { VStack {

View File

@@ -85,6 +85,7 @@ struct MonthView: View {
} }
.onAppear(perform: { .onAppear(perform: {
EventLogger.log(event: "show_month_view") EventLogger.log(event: "show_month_view")
iapManager.decideShowIAP()
}) })
.padding([.top]) .padding([.top])
.background( .background(

View File

@@ -17,9 +17,12 @@ struct PurchaseButtonView: View {
} }
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor @AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
var iapManager: IAPManager var iapManager: IAPManager
private let height: Float private let height: Float
private let showCountdownTimer: Bool
private let showManageSubClosure: (() -> Void)? private let showManageSubClosure: (() -> Void)?
@State var isPurchasing: Bool = false @State var isPurchasing: Bool = false
@@ -30,10 +33,11 @@ struct PurchaseButtonView: View {
} }
} }
public init(height: Float, iapManager: IAPManager, showManageSubClosure: (() -> Void)? = nil) { public init(height: Float, iapManager: IAPManager, showManageSubClosure: (() -> Void)? = nil, showCountdownTimer: Bool = false) {
self.height = height self.height = height
self.showManageSubClosure = showManageSubClosure self.showManageSubClosure = showManageSubClosure
self.iapManager = iapManager self.iapManager = iapManager
self.showCountdownTimer = showCountdownTimer
} }
var body: some View { var body: some View {
@@ -95,6 +99,7 @@ struct PurchaseButtonView: View {
private var buyOptionsView: some View { private var buyOptionsView: some View {
VStack { VStack {
ZStack { ZStack {
theme.currentTheme.secondaryBGColor
VStack(spacing: 20) { VStack(spacing: 20) {
Text(String(localized: "purchase_view_title")) Text(String(localized: "purchase_view_title"))
.font(.body) .font(.body)
@@ -102,6 +107,34 @@ struct PurchaseButtonView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
if showCountdownTimer {
if let date = Calendar.current.date(byAdding: .day, value: 30, to: firstLaunchDate) {
HStack {
if iapManager.daysLeftBeforeIAP > 0 {
Text(String(localized: "purchase_view_current_subscription_expires_in"))
.font(.body)
.bold()
.foregroundColor(textColor)
Text(date, style: .relative)
.font(.body)
.bold()
.foregroundColor(textColor)
} else {
Text(String(localized: "purchase_view_current_subscription_expired_on"))
.font(.body)
.bold()
.foregroundColor(textColor)
Text(date, style: .date)
.font(.body)
.bold()
.foregroundColor(textColor)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
HStack { HStack {
ForEach(iapManager.sortedSubscriptionKeysByPriceOptions) { product in ForEach(iapManager.sortedSubscriptionKeysByPriceOptions) { product in
Button(action: { Button(action: {

View File

@@ -28,6 +28,7 @@ struct SettingsView: View {
@AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor @AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -56,6 +57,8 @@ struct SettingsView: View {
// fixWeekday // fixWeekday
exportData exportData
importData importData
editFirstLaunchDatePast
resetLaunchDate
Divider() Divider()
} }
Spacer() Spacer()
@@ -73,6 +76,7 @@ struct SettingsView: View {
} }
.onAppear(perform: { .onAppear(perform: {
EventLogger.log(event: "show_settings_view") EventLogger.log(event: "show_settings_view")
iapManager.setUpdateTimer()
}) })
.background( .background(
theme.currentTheme.bg theme.currentTheme.bg
@@ -142,12 +146,12 @@ struct SettingsView: View {
private var subscriptionInfoView: some View { private var subscriptionInfoView: some View {
ZStack { ZStack {
theme.currentTheme.secondaryBGColor theme.currentTheme.secondaryBGColor
PurchaseButtonView(height: iapManager.currentSubscription != nil ? 300 : 175, iapManager: iapManager, showManageSubClosure: { PurchaseButtonView(height: iapManager.currentSubscription != nil ? 300 : 200, iapManager: iapManager, showManageSubClosure: {
Task { Task {
await await
self.showManageSubscription() self.showManageSubscription()
} }
}) }, showCountdownTimer: true)
} }
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
@@ -157,6 +161,7 @@ struct SettingsView: View {
Spacer() Spacer()
Button(action: { Button(action: {
EventLogger.log(event: "tap_settings_close") EventLogger.log(event: "tap_settings_close")
iapManager.decideShowIAP()
dismiss() dismiss()
}, label: { }, label: {
Text(String(localized: "settings_view_exit")) Text(String(localized: "settings_view_exit"))
@@ -214,6 +219,41 @@ struct SettingsView: View {
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
} }
private var editFirstLaunchDatePast: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: {
var tmpDate = Date()
tmpDate = Calendar.current.date(byAdding: .day, value: -29, to: tmpDate)!
tmpDate = Calendar.current.date(byAdding: .hour, value: -23, to: tmpDate)!
tmpDate = Calendar.current.date(byAdding: .minute, value: -59, to: tmpDate)!
tmpDate = Calendar.current.date(byAdding: .second, value: -45, to: tmpDate)!
firstLaunchDate = tmpDate
}, label: {
Text("Set first launch date back 29 days, 23 hrs, 45 seconds")
.foregroundColor(textColor)
})
.padding()
}
.fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
}
private var resetLaunchDate: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: {
firstLaunchDate = Date()
}, label: {
Text("Reset luanch date to current date")
.foregroundColor(textColor)
})
.padding()
}
.fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
}
private var clearDB: some View { private var clearDB: some View {
ZStack { ZStack {
theme.currentTheme.secondaryBGColor theme.currentTheme.secondaryBGColor

View File

@@ -66,6 +66,7 @@ struct YearView: View {
} }
.onAppear(perform: { .onAppear(perform: {
self.viewModel.filterEntries(startDate: Date(timeIntervalSince1970: 0), endDate: Date()) self.viewModel.filterEntries(startDate: Date(timeIntervalSince1970: 0), endDate: Date())
iapManager.decideShowIAP()
}) })
.background( .background(
theme.currentTheme.bg theme.currentTheme.bg

View File

@@ -112,6 +112,8 @@
"purchase_view_other_options" = "Other Options"; "purchase_view_other_options" = "Other Options";
"purchase_view_cancel" = "Cancel"; "purchase_view_cancel" = "Cancel";
"purchase_view_current_subscription" = "Current Subscription"; "purchase_view_current_subscription" = "Current Subscription";
"purchase_view_current_subscription_expires_in" = "Trial expires in:";
"purchase_view_current_subscription_expired_on" = "Trial expired on:";
/* not used */ /* not used */
"onboarding_title_title" = "What would you like the reminder to say?"; "onboarding_title_title" = "What would you like the reminder to say?";