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
@StateObject var iapManager = IAPManager()
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
init() {
BGTaskScheduler.shared.cancelAllTaskRequests()
BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in

View File

@@ -20,7 +20,8 @@ public enum StoreError: Error {
class IAPManager: ObservableObject {
@Published private(set) var showIAP = false
@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] {
subscriptions.keys.sorted(by: {
$0.price < $1.price
@@ -74,23 +75,52 @@ class IAPManager: ObservableObject {
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([
"com.88oakapps.ifeel.IAP.subscription.weekly",
"com.88oakapps.ifeel.IAP.subscription.monthly",
"com.88oakapps.ifeel.IAP.subscription.yearly"
])
var expireOnTimer: Timer?
init() {
//Start a transaction listener as close to app launch as possible so you don't miss any transactions.
updateListenerTask = listenForTransactions()
refresh()
setUpdateTimer()
}
deinit {
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() {
Task {
//During store initialization, request products from the App Store.
@@ -104,16 +134,25 @@ class IAPManager: ObservableObject {
}
func decideShowIAP() {
guard !subscriptions.isEmpty else {
return
}
var tmpShowIAP = true
// if we have a sub in the subscriptions dict
// then we dont need to show
for (_, value) in self.subscriptions {
if value != nil {
tmpShowIAP = false
DispatchQueue.main.async {
self.showIAP = false
}
return
}
}
var tmpShowIAP = true
// if its passed 30 days with no sub
if daysLeftBeforeIAP <= 0 {
tmpShowIAP = true
} else {
tmpShowIAP = false
}
DispatchQueue.main.async {
self.showIAP = tmpShowIAP
}

View File

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

View File

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

View File

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

View File

@@ -85,6 +85,7 @@ struct MonthView: View {
}
.onAppear(perform: {
EventLogger.log(event: "show_month_view")
iapManager.decideShowIAP()
})
.padding([.top])
.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.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
private let height: Float
private let showCountdownTimer: Bool
private let showManageSubClosure: (() -> Void)?
@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.showManageSubClosure = showManageSubClosure
self.iapManager = iapManager
self.showCountdownTimer = showCountdownTimer
}
var body: some View {
@@ -95,6 +99,7 @@ struct PurchaseButtonView: View {
private var buyOptionsView: some View {
VStack {
ZStack {
theme.currentTheme.secondaryBGColor
VStack(spacing: 20) {
Text(String(localized: "purchase_view_title"))
.font(.body)
@@ -102,6 +107,34 @@ struct PurchaseButtonView: View {
.foregroundColor(textColor)
.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 {
ForEach(iapManager.sortedSubscriptionKeysByPriceOptions) { product in
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.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.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
var body: some View {
ScrollView {
@@ -56,6 +57,8 @@ struct SettingsView: View {
// fixWeekday
exportData
importData
editFirstLaunchDatePast
resetLaunchDate
Divider()
}
Spacer()
@@ -73,6 +76,7 @@ struct SettingsView: View {
}
.onAppear(perform: {
EventLogger.log(event: "show_settings_view")
iapManager.setUpdateTimer()
})
.background(
theme.currentTheme.bg
@@ -142,12 +146,12 @@ struct SettingsView: View {
private var subscriptionInfoView: some View {
ZStack {
theme.currentTheme.secondaryBGColor
PurchaseButtonView(height: iapManager.currentSubscription != nil ? 300 : 175, iapManager: iapManager, showManageSubClosure: {
PurchaseButtonView(height: iapManager.currentSubscription != nil ? 300 : 200, iapManager: iapManager, showManageSubClosure: {
Task {
await
self.showManageSubscription()
}
})
}, showCountdownTimer: true)
}
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
}
@@ -157,6 +161,7 @@ struct SettingsView: View {
Spacer()
Button(action: {
EventLogger.log(event: "tap_settings_close")
iapManager.decideShowIAP()
dismiss()
}, label: {
Text(String(localized: "settings_view_exit"))
@@ -214,6 +219,41 @@ struct SettingsView: View {
.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 {
ZStack {
theme.currentTheme.secondaryBGColor

View File

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

View File

@@ -112,6 +112,8 @@
"purchase_view_other_options" = "Other Options";
"purchase_view_cancel" = "Cancel";
"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 */
"onboarding_title_title" = "What would you like the reminder to say?";