diff --git a/HabitRPG/Generated/Assets-Images.swift b/HabitRPG/Generated/Assets-Images.swift index 1288d9b0d..f8d2087ff 100644 --- a/HabitRPG/Generated/Assets-Images.swift +++ b/HabitRPG/Generated/Assets-Images.swift @@ -210,6 +210,7 @@ internal enum Asset { internal static let gemcapLeft = ImageAsset(name: "gemcap_left") internal static let gemcapRight = ImageAsset(name: "gemcap_right") internal static let giftSubGift = ImageAsset(name: "gift_sub_gift") + internal static let giftSubscriptionHills = ImageAsset(name: "gift_subscription_hills") internal static let grabIndicator = ImageAsset(name: "grab_indicator") internal static let gryphon = ImageAsset(name: "gryphon") internal static let hourglassBannerLeft = ImageAsset(name: "hourglass_banner_left") diff --git a/HabitRPG/Generated/Strings.swift b/HabitRPG/Generated/Strings.swift index 7ed66d7d3..58c5901c9 100644 --- a/HabitRPG/Generated/Strings.swift +++ b/HabitRPG/Generated/Strings.swift @@ -114,7 +114,7 @@ public enum L10n { } /// Cancel public static var cancel: String { return L10n.tr("Mainstrings", "cancel") } - /// Cancel Subscription + /// Cancel subscription public static var cancelSubscription: String { return L10n.tr("Mainstrings", "cancel_subscription") } /// You have a free subscription because you are a member a Party or Guild that has a Group Plan. This will end if you leave or the Group Plan is cancelled by the owner. Any months of extra subscription credit you have will be applied at the end of the Group Plan. public static var cancelSubscriptionGroupPlan: String { return L10n.tr("Mainstrings", "cancel_subscription_group_plan") } @@ -326,6 +326,8 @@ public enum L10n { public static var earnedAchievementShare: String { return L10n.tr("Mainstrings", "earned_achievement_share") } /// Edit public static var edit: String { return L10n.tr("Mainstrings", "edit") } + /// Edit or cancel subscription + public static var editCancelSubscription: String { return L10n.tr("Mainstrings", "edit_cancel_subscription") } /// Challenge tasks only offer limited editing. public static var editChallengeTasks: String { return L10n.tr("Mainstrings", "edit_challenge_tasks") } /// Edit Tag @@ -470,6 +472,8 @@ public enum L10n { } /// Gift One, Get One Event public static var giftOneGetOneEvent: String { return L10n.tr("Mainstrings", "gift_one_get_one_event") } + /// While this promotion is active, you’ll receive a matching subscription automatically after sending your gift. + public static var giftOneGetOneScreenBanner: String { return L10n.tr("Mainstrings", "gift_one_get_one_screen_banner") } /// Gift One, Get One public static var giftOneGetOneTitle: String { return L10n.tr("Mainstrings", "gift_one_get_one_title") } /// Enter recipient's @ username @@ -950,9 +954,9 @@ public enum L10n { public static var remove: String { return L10n.tr("Mainstrings", "remove") } /// Renew Subscription public static var renewSubscription: String { return L10n.tr("Mainstrings", "renew_subscription") } - /// Want to continue receiving subscription benefits? You can restart your subscription by going to the Settings app and viewing your Apple ID subscriptions. + /// Want to continue receiving subscription benefits? You can restart your subscription from the subscriptions section of your Apple ID settings. public static var renewSubscriptionDescription: String { return L10n.tr("Mainstrings", "renew_subscription_description") } - /// Want to continue receiving subscription benefits? You can start a recurring subscription by going to the Settings app and viewing your Apple ID subscriptions. + /// Want to continue receiving subscription benefits? You can start a recurring subscription from the subscriptions section of your Apple ID settings. public static var renewSubscriptionGiftedDescription: String { return L10n.tr("Mainstrings", "renew_subscription_gifted_description") } /// Repeat Password public static var repeatPassword: String { return L10n.tr("Mainstrings", "repeat_password") } @@ -1230,11 +1234,11 @@ public enum L10n { public static var unshadowMuteUser: String { return L10n.tr("Mainstrings", "unshadow_mute_user") } /// Do you want to remove the shadow ban from this user? public static var unshadowMuteUserConfirm: String { return L10n.tr("Mainstrings", "unshadow_mute_user_confirm") } - /// No longer want to subscribe? Due to your payment method, you can only unsubscribe through the Google Play Store. + /// No longer want to subscribe? Due to your payment method, you’ll need to unsubscribe through the Google Play Store. Any leftover months of subscription credit will be added to your end date after cancellation. public static var unsubscribeGoogle: String { return L10n.tr("Mainstrings", "unsubscribe_google") } - /// No longer want to subscribe? You can find the option to unsubscribe by going to the Settings app and viewing your Apple ID subscriptions + /// Want to change your subscription duration, or cancel all together? You can edit or cancel your subscription by going to the Settings app and viewing your Apple ID subscriptions. Any leftover months of subscription credit will be added to your end date after cancellation. public static var unsubscribeItunes: String { return L10n.tr("Mainstrings", "unsubscribe_itunes") } - /// No longer want to subscribe? Due to your payment method, you can only unsubscribe through the website. + /// No longer want to subscribe? Due to your payment method, you’ll need to unsubscribe from our website. To do this, tap the button below, log in to your account, tap the player icon in the top right, then go to the Subscription section. Any leftover months of subscription credit will be added to your end date after cancellation. We’ll miss you! public static var unsubscribeWebsite: String { return L10n.tr("Mainstrings", "unsubscribe_website") } /// Update public static var update: String { return L10n.tr("Mainstrings", "update") } @@ -2765,7 +2769,7 @@ public enum L10n { public static var gemForGoldHeader: String { return L10n.tr("Mainstrings", "subscription.gem_for_gold_header") } /// Gifted public static var gifted: String { return L10n.tr("Mainstrings", "subscription.gifted") } - /// Subscribers get Mystic Hourglasses to buy items in the Time Travelers Shop and these other exclusive benefits! + /// Subscribers get a Mystic Hourglass each month along with these exclusive benefits! public static var hourglassesHeader: String { return L10n.tr("Mainstrings", "subscription.hourglasses_header") } /// Get two chances to score new equipment each time you open the Enchanted Armoire! public static var infoArmoireDescription: String { return L10n.tr("Mainstrings", "subscription.info_armoire_description") } diff --git a/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Contents.json b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Contents.json new file mode 100644 index 000000000..d0e9993fb --- /dev/null +++ b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Gifting screen hills.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Gifting screen hills@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Gifting screen hills@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills.png b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills.png new file mode 100644 index 000000000..ebd0a34ea Binary files /dev/null and b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills.png differ diff --git a/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@2x.png b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@2x.png new file mode 100644 index 000000000..1eca3939d Binary files /dev/null and b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@2x.png differ diff --git a/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@3x.png b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@3x.png new file mode 100644 index 000000000..0e66be2cd Binary files /dev/null and b/HabitRPG/Images.xcassets/gift_subscription_hills.imageset/Gifting screen hills@3x.png differ diff --git a/HabitRPG/Strings/Base.lproj/Mainstrings.strings b/HabitRPG/Strings/Base.lproj/Mainstrings.strings index 2ec9db88a..9be9014ec 100644 --- a/HabitRPG/Strings/Base.lproj/Mainstrings.strings +++ b/HabitRPG/Strings/Base.lproj/Mainstrings.strings @@ -627,11 +627,11 @@ "inactive" = "Inactive"; "one_month" = "1 Month"; "x_months" = "%d Months"; -"unsubscribe_itunes" = "No longer want to subscribe? You can find the option to unsubscribe by going to the Settings app and viewing your Apple ID subscriptions"; +"unsubscribe_itunes" = "Want to change your subscription duration, or cancel all together? You can edit or cancel your subscription by going to the Settings app and viewing your Apple ID subscriptions. Any leftover months of subscription credit will be added to your end date after cancellation."; "open_itunes" = "Open Apple ID Subscriptions"; "open_app_store" = "Open App Store Page"; -"unsubscribe_website" = "No longer want to subscribe? Due to your payment method, you can only unsubscribe through the website."; -"unsubscribe_google" = "No longer want to subscribe? Due to your payment method, you can only unsubscribe through the Google Play Store."; +"unsubscribe_website" = "No longer want to subscribe? Due to your payment method, you’ll need to unsubscribe from our website. To do this, tap the button below, log in to your account, tap the player icon in the top right, then go to the Subscription section. Any leftover months of subscription credit will be added to your end date after cancellation. We’ll miss you!"; +"unsubscribe_google" = "No longer want to subscribe? Due to your payment method, you’ll need to unsubscribe through the Google Play Store. Any leftover months of subscription credit will be added to your end date after cancellation."; "group_plan" = "Group Plan"; "member_group_plan" = "Member of a Group Plan"; "open_website" = "Open Habitica Website"; @@ -835,7 +835,7 @@ "faint.dont_despair" = "Don't despair!"; "faint.good_luck_text" = "But you can get them all back with hard work! Good luck—you’ll do great. "; "faint.button" = "Refill Health & Try Again"; -"faint.disclaimer" = "Broken equipment can be repurchased from Rewards"; +"faint.disclaimer" = "Broken equipment can be repurchased from the Market"; "faint.unsubbed_button_prompt" = "Subscribe to hold on with 1HP!"; "faint.unsubbed_footer" = "Get a second chance each day to avoid running out of HP with a subscription"; "faint.subbed_button_prompt" = "Second chance: Hold on with 1HP!"; @@ -958,7 +958,7 @@ "subscription.armore_header" = "Subscribers get extra chances at the Armoire and these other benefits!"; "subscription.faint_header" = "Subscribers get a second chance at life each day and these other benefits!"; "subscription.gem_for_gold_header" = "Subscribe to buy Gems with Gold and receive these other exclusive benefits!"; -"subscription.hourglasses_header" = "Subscribers get Mystic Hourglasses to buy items in the Time Travelers Shop and these other exclusive benefits!"; +"subscription.hourglasses_header" = "Subscribers get a Mystic Hourglass each month along with these exclusive benefits!"; "subscription.stay_motivated_with_more_rewards" = "Stay motivated with even more rewards when you subscribe"; "subscription.thanks_for_subscribing" = "Thanks for subscribing"; "subscription.enjoy_these_benefits" = "Enjoy all these exclusive benefits with your subscription"; @@ -980,9 +980,10 @@ "ending_on" = "Ending on %@"; "not_recurring" = "Not Recurring"; "cancelled" = "Cancelled"; -"cancel_subscription" = "Cancel Subscription"; -"renew_subscription_description" = "Want to continue receiving subscription benefits? You can restart your subscription by going to the Settings app and viewing your Apple ID subscriptions."; -"renew_subscription_gifted_description" = "Want to continue receiving subscription benefits? You can start a recurring subscription by going to the Settings app and viewing your Apple ID subscriptions."; +"cancel_subscription" = "Cancel subscription"; +"edit_cancel_subscription" = "Edit or cancel subscription"; +"renew_subscription_description" = "Want to continue receiving subscription benefits? You can restart your subscription from the subscriptions section of your Apple ID settings."; +"renew_subscription_gifted_description" = "Want to continue receiving subscription benefits? You can start a recurring subscription from the subscriptions section of your Apple ID settings."; "cancel_subscription_group_plan" = "You have a free subscription because you are a member a Party or Guild that has a Group Plan. This will end if you leave or the Group Plan is cancelled by the owner. Any months of extra subscription credit you have will be applied at the end of the Group Plan."; "send_gift" = "Send Gift"; "gift_one_get_one" = "Gift a sub and get a sub free event going on now!"; @@ -1251,6 +1252,7 @@ "gift_one_get_one_data.info_limitations" = "This is a limited time event that starts on %@ (%@ UTC) and will end %@ (%@ UTC). This promotion only applies when you gift to another Habitican. If you or your gift recipient already have a subscription, the gifted subscription will add months of credit that will only be used after the current subscription is cancelled or expires."; "gift_one_get_one_data.purchase_banner_title" = "Gift a sub and get a sub for free until %@"; "gift_confirmation_body_g1_g1" = "You sent %@ a %@-month Habitica subscription and the same subscription was applied to your account for our Gift One Get One promotion!"; +"gift_one_get_one_screen_banner" = "While this promotion is active, you’ll receive a matching subscription automatically after sending your gift."; "create_guild" = "Create Guild"; "create_guild_description" = "To create a Guild, log in to the Habitica website then tap the “Create” button on the “My Guilds” screen."; "create_challenge" = "Create Challenge"; diff --git a/HabitRPG/UI/General/FaintViewController.swift b/HabitRPG/UI/General/FaintViewController.swift index 9c8c65166..2d2701749 100644 --- a/HabitRPG/UI/General/FaintViewController.swift +++ b/HabitRPG/UI/General/FaintViewController.swift @@ -337,11 +337,12 @@ class FaintViewController: UIHostingController { rootView.onDismiss = {[weak self] in self?.dismiss() } + + SoundManager.shared.play(effect: .death) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - SoundManager.shared.play(effect: .death) } private func dismiss() { diff --git a/HabitRPG/UI/Promo/BirthdayviewController.swift b/HabitRPG/UI/Promo/BirthdayviewController.swift index 323eedd7d..a13817c92 100644 --- a/HabitRPG/UI/Promo/BirthdayviewController.swift +++ b/HabitRPG/UI/Promo/BirthdayviewController.swift @@ -103,7 +103,7 @@ private struct FourFreeView: View { struct BirthdayView: View { private let dateFormatter = { let formatter = DateFormatter() - formatter.dateFormat = "MMM dd" + formatter.dateFormat = "MMM d" return formatter }() private let complexDateFormatter = { diff --git a/HabitRPG/UI/Promo/PromotionInfoViewController.swift b/HabitRPG/UI/Promo/PromotionInfoViewController.swift index f1928934a..9449df015 100644 --- a/HabitRPG/UI/Promo/PromotionInfoViewController.swift +++ b/HabitRPG/UI/Promo/PromotionInfoViewController.swift @@ -123,7 +123,9 @@ class PromotionInfoViewController: BaseUIViewController { private func showGiftSubscriptionAlert() { let navController = EditingFormViewController.buildWithUsernameField(title: L10n.giftRecipientTitle, subtitle: L10n.giftRecipientSubtitle, onSave: { username in - RouterHandler.shared.handle(.giftSubscription(username: username)) + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { + RouterHandler.shared.handle(.giftSubscription(username: username)) + }) }, saveButtonTitle: L10n.continue) present(navController, animated: true, completion: nil) } diff --git a/HabitRPG/UI/Purchases/G1G1Banner.swift b/HabitRPG/UI/Purchases/G1G1Banner.swift index 51776d5f6..5b4596e0c 100644 --- a/HabitRPG/UI/Purchases/G1G1Banner.swift +++ b/HabitRPG/UI/Purchases/G1G1Banner.swift @@ -14,7 +14,7 @@ struct G1G1Banner: View { private let formatter = { let formatter = DateFormatter() - formatter.dateFormat = "MMM dd" + formatter.dateFormat = "MMM d" return formatter }() @@ -35,7 +35,7 @@ struct G1G1Banner: View { } .background(LinearGradient(colors: [Color(UIColor("#3BCAD7")), Color(UIColor("#925CF3"))], startPoint: .topLeading, endPoint: .bottomTrailing)) .onTapGesture { - + RouterHandler.shared.handle(.promoInfo) } } } diff --git a/HabitRPG/UI/Purchases/GiftSubscriptionViewController.swift b/HabitRPG/UI/Purchases/GiftSubscriptionViewController.swift index 73d06b5e6..dc3c47c58 100644 --- a/HabitRPG/UI/Purchases/GiftSubscriptionViewController.swift +++ b/HabitRPG/UI/Purchases/GiftSubscriptionViewController.swift @@ -98,7 +98,25 @@ struct GiftSubscriptionPage: View { .padding(.horizontal, 24) .padding(.top, 16) .padding(.bottom, 32) - Image(Asset.subscriptionBackground.name) + Image(Asset.giftSubscriptionHills.name) + if viewModel.isGiftOneGetOne() { + VStack(alignment: .center) { + HStack { + Image(Asset.g1g1SparklesLeft.name) + Text(L10n.giftOneGetOneTitle) + .font(.system(size: 17, weight: .medium)) + Image(Asset.g1g1SparklesRight.name) + }.padding(.top, 16) + Text(L10n.giftOneGetOneScreenBanner) + .font(.system(size: 13, weight: .semibold)) + .lineSpacing(2) + }.foregroundColor(.white) + .frame(maxWidth: .infinity) + .background(LinearGradient(colors: [ + Color(hexadecimal: "50B5E9"), + Color(hexadecimal: "925CF3") + ], startPoint: .top, endPoint: .bottom)) + } }.background(Color.purple300.ignoresSafeArea(.all, edges: .top).padding(.bottom, 4)) }.foregroundColor(.white) .background(Color.purple400.ignoresSafeArea(.all, edges: .bottom).padding(.top, 200)) @@ -106,15 +124,23 @@ struct GiftSubscriptionPage: View { } class GiftSubscriptionViewModel: BaseSubscriptionViewModel { + let configRepository = ConfigRepository.shared + @Published var giftedUser: MemberProtocol? @Published var selectedSubscription: String = PurchaseHandler.noRenewSubscriptionIdentifiers[3] @Published var availableSubscriptions = PurchaseHandler.noRenewSubscriptionIdentifiers var onSuccessfulSubscribe: (() -> Void)? - + @Published var activePromo: HabiticaPromotion? + override init() { super.init() retrieveProductList() + activePromo = configRepository.activePromotion() + } + + func isGiftOneGetOne() -> Bool { + return activePromo?.identifier == "g1g1" } func retrieveProductList() { diff --git a/HabitRPG/UI/Purchases/SubscriptionDetailViewUI.swift b/HabitRPG/UI/Purchases/SubscriptionDetailViewUI.swift index 8eaf2eb5a..02177d047 100644 --- a/HabitRPG/UI/Purchases/SubscriptionDetailViewUI.swift +++ b/HabitRPG/UI/Purchases/SubscriptionDetailViewUI.swift @@ -256,6 +256,8 @@ struct SubscriptionDetailViewUI: View { Text(L10n.continueBenefits) } else if plan.dateTerminated != nil { Text(L10n.resubscribe) + } else if plan.paymentMethod == "Apple" { + Text(L10n.editCancelSubscription) } else { Text(L10n.cancelSubscription) } diff --git a/HabitRPG/UI/Purchases/SubscriptionPage.swift b/HabitRPG/UI/Purchases/SubscriptionPage.swift index f2b29a25a..2588161d9 100644 --- a/HabitRPG/UI/Purchases/SubscriptionPage.swift +++ b/HabitRPG/UI/Purchases/SubscriptionPage.swift @@ -289,9 +289,7 @@ struct SubscriptionBenefitListView: View { SubscriptionBenefitView(icon: PixelArtView(name: "shop_set_mystery_\(mysteryGear?.key?.split(separator: "_").last ?? "")"), title: Text(L10n.subscriptionInfo3Title), description: Text(mysteryGear?.text != nil ? L10n.subscriptionInfo3DescriptionGear(dateFormatter.string(from: Date()), mysteryGear?.text ?? "") : L10n.subscriptionInfo3Description)) - if presentationPoint != .timetravelers { - SubscriptionBenefitView(icon: Image(Asset.subBenefitsHourglasses.name), title: Text(L10n.subscriptionInfo2Title), description: Text(L10n.subscriptionInfo2Description)) - } + SubscriptionBenefitView(icon: Image(Asset.subBenefitsHourglasses.name), title: Text(L10n.subscriptionInfo2Title), description: Text(L10n.subscriptionInfo2Description)) if presentationPoint != .faint { SubscriptionBenefitView(icon: Image(Asset.subBenefitsFaint.name), title: Text(L10n.Subscription.infoFaintTitle), description: Text(L10n.Subscription.infoFaintDescription)) } @@ -439,7 +437,7 @@ struct SubscriptionPage: View { .padding(.vertical, 13) .padding(.horizontal, 24) Text(L10n.subscriptionSupportDevelopers) - .foregroundColor(Color(UIColor.purple600)) + .foregroundColor(.white) .font(.system(size: 13)) .italic() .multilineTextAlignment(.center) @@ -488,7 +486,6 @@ struct SubscriptionPage: View { if viewModel.presentationPoint == nil { if viewModel.isRestoringPurchase { ProgressView().habiticaProgressStyle().frame(height: 48) - .transition(.opacity) } else { Button { viewModel.checkForExistingSubscription() @@ -496,9 +493,10 @@ struct SubscriptionPage: View { Text(L10n.restorePurchase) .foregroundColor(.yellow100) .font(.system(size: 17, weight: .semibold)) + .animation(nil) } .frame(height: 48) - .transition(.opacity) + .animation(nil) } } else { Button { diff --git a/HabitRPG/UI/Purchases/SubscriptionViewController.swift b/HabitRPG/UI/Purchases/SubscriptionViewController.swift index 276800e91..715dec40b 100644 --- a/HabitRPG/UI/Purchases/SubscriptionViewController.swift +++ b/HabitRPG/UI/Purchases/SubscriptionViewController.swift @@ -161,7 +161,7 @@ class SubscriptionViewController: BaseTableViewController { navigationController?.navigationBar.standardAppearance.backgroundColor = theme.contentBackgroundColor navigationController?.navigationBar.shadowImage = UIImage() giftSubscriptionExplanationLabel.textColor = theme.ternaryTextColor - subscriptionSupportLabel.textColor = theme.secondaryTextColor + subscriptionSupportLabel.textColor = .white if theme.isDark { headerImage.image = Asset.subscribeHeaderDark.image diff --git a/HabitRPG/Utilities/RouterHandler.swift b/HabitRPG/Utilities/RouterHandler.swift index d25374f8f..bc5f007d6 100644 --- a/HabitRPG/Utilities/RouterHandler.swift +++ b/HabitRPG/Utilities/RouterHandler.swift @@ -63,6 +63,8 @@ enum Route { case customizations(type: String, group: String? = nil) + case promoInfo + var url: String { // swiftlint:disable switch self { @@ -81,6 +83,7 @@ enum Route { } else { return "/inventory/customizations/\(type)" } + case .promoInfo: return "/promo/info" } // siwftlint:enable } @@ -253,9 +256,6 @@ class RouterHandler { register("/user/settings/gems") { self.present(StoryboardScene.Main.purchaseGemNavController.instantiate()) } - register("/promo") { - self.present(StoryboardScene.Main.promotionInfoNavController.instantiate()) - } register("/private-messages") { self.displayTab(index: 4) self.present(StoryboardScene.Social.inboxNavigationViewController.instantiate()) @@ -283,7 +283,7 @@ class RouterHandler { register("/user/onboarding") { self.present(StoryboardScene.Main.adventureGuideNavigationViewController.instantiate()) } - register("/promo/info") { + register(.promoInfo) { self.present(StoryboardScene.Main.promotionInfoNavController.instantiate()) } register("/promo/web") {