diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0cfa396e35..09938979c3 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -1039,6 +1039,7 @@ F97A2EEA282578C000610669 /* BadgeExpirationSheetStateTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97A2EE828247C1300610669 /* BadgeExpirationSheetStateTest.swift */; }; F9A8ACC7280A175E00AFC6A7 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A8ACC6280A175E00AFC6A7 /* DonationViewController.swift */; }; F9BC0A2527FB8E730085B23D /* AppSettingsViewsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9BC0A2427FB8E730085B23D /* AppSettingsViewsUtil.swift */; }; + F9D83012282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D83011282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift */; }; F9DD70B92811AF82000C5960 /* DonationViewsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */; }; FCB11D8C1A129A76002F93FB /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB11D8B1A129A76002F93FB /* CoreMedia.framework */; }; /* End PBXBuildFile section */ @@ -2425,6 +2426,7 @@ F97A2EE828247C1300610669 /* BadgeExpirationSheetStateTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeExpirationSheetStateTest.swift; sourceTree = ""; }; F9A8ACC6280A175E00AFC6A7 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = ""; }; F9BC0A2427FB8E730085B23D /* AppSettingsViewsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsViewsUtil.swift; sourceTree = ""; }; + F9D83011282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeGiftingChooseBadgeViewController.swift; sourceTree = ""; }; F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewsUtil.swift; sourceTree = ""; }; FC3BD9871A30A790005B96BB /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; FCB11D8B1A129A76002F93FB /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; @@ -2678,22 +2680,14 @@ 340FC87A204DAC8C007AEB0F /* AppSettings */ = { isa = PBXGroup; children = ( + F9C5BC5F282E98D4001EB3BF /* Donations */, F9BC0A2427FB8E730085B23D /* AppSettingsViewsUtil.swift */, - F900F2DC27F25AB300431E09 /* DonationReceiptViewController.swift */, - F9066F0627ECE41B008C9530 /* DonationReceiptsViewController.swift */, 88F58A1625EEE5B9008CDA24 /* AppSettingsViewController.swift */, 887B6DCA25F6C3F500E677D4 /* Account */, 887B6DCB25F6C40500E677D4 /* Appearance */, - 888017912741F33B00346E9A /* BoostViewController.swift */, - 8880179327430DDB00346E9A /* BadgeThanksSheet.swift */, - 881BB2892743531D00B609B8 /* BadgeDetailsSheet.swift */, - F02564D7274EDF4600D7B48A /* BadgeExpirationSheet.swift */, 887B380725F0355700685845 /* ChatsSettingsViewController.swift */, 3414896825C9B6490098E3ED /* CurrencyPickerViewController.swift */, 887B6DCC25F6C41500E677D4 /* Data Usage */, - F0CA67F92728A89A002B9DFE /* SubscriptionViewController.swift */, - F9A8ACC6280A175E00AFC6A7 /* DonationViewController.swift */, - F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */, 32ECD08624BFBF5C00EDDED0 /* Help */, 887B6DCE25F6C45B00E677D4 /* Internal */, 887B6DCD25F6C44800E677D4 /* Linked Devices */, @@ -4775,6 +4769,23 @@ path = "Context Menus"; sourceTree = ""; }; + F9C5BC5F282E98D4001EB3BF /* Donations */ = { + isa = PBXGroup; + children = ( + F900F2DC27F25AB300431E09 /* DonationReceiptViewController.swift */, + F9066F0627ECE41B008C9530 /* DonationReceiptsViewController.swift */, + 888017912741F33B00346E9A /* BoostViewController.swift */, + 8880179327430DDB00346E9A /* BadgeThanksSheet.swift */, + 881BB2892743531D00B609B8 /* BadgeDetailsSheet.swift */, + F02564D7274EDF4600D7B48A /* BadgeExpirationSheet.swift */, + F0CA67F92728A89A002B9DFE /* SubscriptionViewController.swift */, + F9A8ACC6280A175E00AFC6A7 /* DonationViewController.swift */, + F9DD70B82811AF82000C5960 /* DonationViewsUtil.swift */, + F9D83011282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift */, + ); + path = Donations; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -6412,6 +6423,7 @@ 34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */, 88535064240829950011D318 /* DateHeaderInteraction.swift in Sources */, 88588D26252E59CE00405414 /* CallService.swift in Sources */, + F9D83012282DBB1500399363 /* BadgeGiftingChooseBadgeViewController.swift in Sources */, 8822558D26B9D1D7001A33C4 /* SignalMe.swift in Sources */, 457C87B82032645C008D52D6 /* DebugUINotifications.swift in Sources */, 34848D6325D44EBD00E5034B /* PaymentsTransferInViewController.swift in Sources */, diff --git a/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/Contents.json b/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/Contents.json new file mode 100644 index 0000000000..13af250cdd --- /dev/null +++ b/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "badge-gifting-promo-image-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/badge-gifting-promo-image-dark.pdf b/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/badge-gifting-promo-image-dark.pdf new file mode 100644 index 0000000000..0d9a0c3c9f Binary files /dev/null and b/Signal/Images.xcassets/badge-gifting-promo-image-dark.imageset/badge-gifting-promo-image-dark.pdf differ diff --git a/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/Contents.json b/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/Contents.json new file mode 100644 index 0000000000..dec69853e5 --- /dev/null +++ b/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "badge-gifting-promo-image-light.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/badge-gifting-promo-image-light.pdf b/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/badge-gifting-promo-image-light.pdf new file mode 100644 index 0000000000..3fe870dd1a Binary files /dev/null and b/Signal/Images.xcassets/badge-gifting-promo-image-light.imageset/badge-gifting-promo-image-light.pdf differ diff --git a/Signal/src/ViewControllers/AppSettings/BadgeDetailsSheet.swift b/Signal/src/ViewControllers/AppSettings/Donations/BadgeDetailsSheet.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/BadgeDetailsSheet.swift rename to Signal/src/ViewControllers/AppSettings/Donations/BadgeDetailsSheet.swift diff --git a/Signal/src/ViewControllers/AppSettings/BadgeExpirationSheet.swift b/Signal/src/ViewControllers/AppSettings/Donations/BadgeExpirationSheet.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/BadgeExpirationSheet.swift rename to Signal/src/ViewControllers/AppSettings/Donations/BadgeExpirationSheet.swift diff --git a/Signal/src/ViewControllers/AppSettings/Donations/BadgeGiftingChooseBadgeViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/BadgeGiftingChooseBadgeViewController.swift new file mode 100644 index 0000000000..67f997555a --- /dev/null +++ b/Signal/src/ViewControllers/AppSettings/Donations/BadgeGiftingChooseBadgeViewController.swift @@ -0,0 +1,73 @@ +// +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. +// + +import Foundation +import UIKit + +// TODO(evanhahn): This screen is not finished. +class BadgeGiftingChooseBadgeViewController: OWSTableViewController2 { + // MARK: - Callbacks + + public override func viewDidLoad() { + super.viewDidLoad() + updateTableContents() + } + + public override func themeDidChange() { + super.themeDidChange() + updateTableContents() + } + + // MARK: - Table contents + + private func updateTableContents() { + self.contents = OWSTableContents(sections: getTableSections()) + } + + private func getTableSections() -> [OWSTableSection] { + let introSection: OWSTableSection = { + let section = OWSTableSection() + section.hasBackground = false + section.customHeaderView = { + let introStack = UIStackView() + introStack.axis = .vertical + introStack.spacing = 12 + + let imageName = Theme.isDarkThemeEnabled ? "badge-gifting-promo-image-dark" : "badge-gifting-promo-image-light" + let imageView = UIImageView(image: UIImage(named: imageName)) + introStack.addArrangedSubview(imageView) + imageView.contentMode = .scaleAspectFit + + let titleLabel = UILabel() + introStack.addArrangedSubview(titleLabel) + titleLabel.text = NSLocalizedString("BADGE_GIFTING_CHOOSE_BADGE_TITLE", + comment: "Title on the screen where you choose a gift badge") + titleLabel.textAlignment = .center + titleLabel.font = UIFont.ows_dynamicTypeTitle2.ows_semibold + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.autoPinWidthToSuperview(withMargin: 26) + + let paragraphLabel = UILabel() + introStack.addArrangedSubview(paragraphLabel) + paragraphLabel.text = NSLocalizedString("BADGE_GIFTING_CHOOSE_BADGE_DESCRIPTION", + comment: "Short paragraph on the screen where you choose a gift badge") + paragraphLabel.textAlignment = .center + paragraphLabel.font = UIFont.ows_dynamicTypeBody + paragraphLabel.numberOfLines = 0 + paragraphLabel.lineBreakMode = .byWordWrapping + paragraphLabel.autoPinWidthToSuperview(withMargin: 26) + + return introStack + }() + return section + }() + + let result: [OWSTableSection] = [introSection] + + // TODO(evanhahn): Add additional sections. + + return result + } +} diff --git a/Signal/src/ViewControllers/AppSettings/BadgeThanksSheet.swift b/Signal/src/ViewControllers/AppSettings/Donations/BadgeThanksSheet.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/BadgeThanksSheet.swift rename to Signal/src/ViewControllers/AppSettings/Donations/BadgeThanksSheet.swift diff --git a/Signal/src/ViewControllers/AppSettings/BoostViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/BoostViewController.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/BoostViewController.swift rename to Signal/src/ViewControllers/AppSettings/Donations/BoostViewController.swift diff --git a/Signal/src/ViewControllers/AppSettings/DonationReceiptViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/DonationReceiptViewController.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/DonationReceiptViewController.swift rename to Signal/src/ViewControllers/AppSettings/Donations/DonationReceiptViewController.swift diff --git a/Signal/src/ViewControllers/AppSettings/DonationReceiptsViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/DonationReceiptsViewController.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/DonationReceiptsViewController.swift rename to Signal/src/ViewControllers/AppSettings/Donations/DonationReceiptsViewController.swift diff --git a/Signal/src/ViewControllers/AppSettings/DonationViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/DonationViewController.swift similarity index 96% rename from Signal/src/ViewControllers/AppSettings/DonationViewController.swift rename to Signal/src/ViewControllers/AppSettings/Donations/DonationViewController.swift index eba5ce499c..d93b075a44 100644 --- a/Signal/src/ViewControllers/AppSettings/DonationViewController.swift +++ b/Signal/src/ViewControllers/AppSettings/Donations/DonationViewController.swift @@ -398,12 +398,26 @@ class DonationViewController: OWSTableViewController2 { } )) - if DonationUtilities.isApplePayAvailable && FeatureFlags.giftBadgeSending { + if DonationUtilities.canSendGiftBadges { section.add(.disclosureItem( icon: .settingsGift, name: NSLocalizedString("DONATION_VIEW_GIFT", comment: "Title for the 'Gift a Badge' link in the donation view"), accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "giftBadge"), - actionBlock: {} + actionBlock: { [weak self] in + guard let self = self else { return } + + // It's possible (but unlikely) to lose the ability to send gifts while this button is + // visible. For example, Apple Pay could be disabled in parental controls after this + // screen is opened. + guard DonationUtilities.canSendGiftBadges else { + // We might want to show a better UI here, but making the button a no-op is + // preferable to launching the view controller. + return + } + + let vc = BadgeGiftingChooseBadgeViewController() + self.navigationController?.pushViewController(vc, animated: true) + } )) } diff --git a/Signal/src/ViewControllers/AppSettings/DonationViewsUtil.swift b/Signal/src/ViewControllers/AppSettings/Donations/DonationViewsUtil.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/DonationViewsUtil.swift rename to Signal/src/ViewControllers/AppSettings/Donations/DonationViewsUtil.swift diff --git a/Signal/src/ViewControllers/AppSettings/SubscriptionViewController.swift b/Signal/src/ViewControllers/AppSettings/Donations/SubscriptionViewController.swift similarity index 100% rename from Signal/src/ViewControllers/AppSettings/SubscriptionViewController.swift rename to Signal/src/ViewControllers/AppSettings/Donations/SubscriptionViewController.swift diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 07bc5e06e6..15c62ae4e0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -391,6 +391,12 @@ /* Title for subscription on the badge expiration sheet. */ "BADGE_EXPIRED_SUBSCRIPTION_TITLE" = "Monthly Donation Cancelled"; +/* Short paragraph on the screen where you choose a gift badge */ +"BADGE_GIFTING_CHOOSE_BADGE_DESCRIPTION" = "Gift someone a badge by making a donation to Signal in their name. They’ll get a badge to display on their profile photo."; + +/* Title on the screen where you choose a gift badge */ +"BADGE_GIFTING_CHOOSE_BADGE_TITLE" = "Gift a Badge"; + /* String explaing to the user that their subscription badge has expired on the badge expiry sheetsheet. Embed {badge name}. */ "BADGE_SUBSCRIPTION_EXPIRED_BECAUSE_OF_CHARGE_FAILURE_BODY_FORMAT" = "Your recurring monthly donation was automatically cancelled because we couldn’t process your payment. Your badge is no longer visible on your profile.\n\nTo continue supporting Signal and to reactivate your badge, update your payment method in Apple Pay and subscribe again."; diff --git a/SignalMessaging/Subscriptions/DonationUtilities.swift b/SignalMessaging/Subscriptions/DonationUtilities.swift index f77a3fe4c1..bf990df280 100644 --- a/SignalMessaging/Subscriptions/DonationUtilities.swift +++ b/SignalMessaging/Subscriptions/DonationUtilities.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// Copyright (c) 2022 Open Whisper Systems. All rights reserved. // import Foundation @@ -11,6 +11,10 @@ public class DonationUtilities: NSObject { PKPaymentAuthorizationController.canMakePayments() } + public static var canSendGiftBadges: Bool { + isApplePayAvailable && FeatureFlags.giftBadgeSending + } + public static var supportedNetworks: [PKPaymentNetwork] { return [ .visa,