Remove legacy donation view
_I recommend reviewing this with whitespace changes disabled._ We have a kill switch that disables badge acquisition. If that kill switch is active, we show a one-time donation view. That kill switch has been off since January of 2021, so I think we can remove this one-time donation view. That lets us remove hundreds of lines of code, but it will also make future donation-related changes a bit easier.
This commit is contained in:
parent
67ea0d0516
commit
ef6c044a54
@ -977,7 +977,6 @@
|
||||
88F58A1725EEE5B9008CDA24 /* AppSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F58A1625EEE5B9008CDA24 /* AppSettingsViewController.swift */; };
|
||||
88F67A0C24E5126D00435A71 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */; };
|
||||
88F7EE93230253C5003ADF7D /* UsernameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F7EE92230253C5003ADF7D /* UsernameViewController.swift */; };
|
||||
88FBE9502649E7EB005F6C80 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FBE94F2649E7EB005F6C80 /* DonationViewController.swift */; };
|
||||
88FBE954264B6849005F6C80 /* SubscriptionMegaphone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FBE953264B6848005F6C80 /* SubscriptionMegaphone.swift */; };
|
||||
88FE237E249C22080041670F /* ConversationViewController+Scroll.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FE237D249C22080041670F /* ConversationViewController+Scroll.swift */; };
|
||||
954AEE6A1DF33E01002E5410 /* ContactsPickerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954AEE681DF33D32002E5410 /* ContactsPickerTest.swift */; };
|
||||
@ -2292,7 +2291,6 @@
|
||||
88EFF4FB25AD4230000FAFBA /* SharingThreadPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingThreadPickerViewController.swift; sourceTree = "<group>"; };
|
||||
88F58A1625EEE5B9008CDA24 /* AppSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
88F7EE92230253C5003ADF7D /* UsernameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameViewController.swift; sourceTree = "<group>"; };
|
||||
88FBE94F2649E7EB005F6C80 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = "<group>"; };
|
||||
88FBE953264B6848005F6C80 /* SubscriptionMegaphone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionMegaphone.swift; sourceTree = "<group>"; };
|
||||
88FE237D249C22080041670F /* ConversationViewController+Scroll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationViewController+Scroll.swift"; sourceTree = "<group>"; };
|
||||
8981C8F64D94D3C52EB67A2C /* Pods-SignalTests.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.test.xcconfig"; sourceTree = "<group>"; };
|
||||
@ -2655,7 +2653,6 @@
|
||||
887B380725F0355700685845 /* ChatsSettingsViewController.swift */,
|
||||
3414896825C9B6490098E3ED /* CurrencyPickerViewController.swift */,
|
||||
887B6DCC25F6C41500E677D4 /* Data Usage */,
|
||||
88FBE94F2649E7EB005F6C80 /* DonationViewController.swift */,
|
||||
F0CA67F92728A89A002B9DFE /* SubscriptionViewController.swift */,
|
||||
32ECD08624BFBF5C00EDDED0 /* Help */,
|
||||
887B6DCE25F6C45B00E677D4 /* Internal */,
|
||||
@ -6359,7 +6356,6 @@
|
||||
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
|
||||
3491899B269CD68E008A18AF /* BlockingAnnouncementOnlyView.swift in Sources */,
|
||||
88D23D2323CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift in Sources */,
|
||||
88FBE9502649E7EB005F6C80 /* DonationViewController.swift in Sources */,
|
||||
34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */,
|
||||
884DB95027DE67BB00C6A309 /* StoryContextViewController.swift in Sources */,
|
||||
34A4D87F2677B23100A794E7 /* ConversationViewController+MessageActions.swift in Sources */,
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "donate_light_dark2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -272,67 +272,54 @@ class AppSettingsViewController: OWSTableViewController2 {
|
||||
self?.showInviteFlow()
|
||||
}
|
||||
))
|
||||
if RemoteConfig.donorBadgeAcquisition {
|
||||
if DonationUtilities.isApplePayAvailable {
|
||||
section4.add(.init(customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = OWSTableItem.buildCellWithAccessoryLabel(
|
||||
icon: .settingsDonate,
|
||||
if DonationUtilities.isApplePayAvailable {
|
||||
section4.add(.init(customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = OWSTableItem.buildCellWithAccessoryLabel(
|
||||
icon: .settingsDonate,
|
||||
itemName: self.hasCurrentSubscription ? NSLocalizedString("SETTINGS_CURRENT_SUBSCRIPTION", comment: "Title for the 'Subscription' link in settings.") : NSLocalizedString("SETTINGS_SUBSCRIPTION", comment: "Title for the 'monthly donation' link in settings."),
|
||||
accessoryType: .disclosureIndicator,
|
||||
accessoryType: .disclosureIndicator,
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "sustainer"))
|
||||
|
||||
if self.hasExpiredSubscription {
|
||||
let imageView = UIImageView(image: UIImage(named: "info-solid-24")?.withRenderingMode(.alwaysTemplate))
|
||||
imageView.tintColor = Theme.accentBlueColor
|
||||
cell.contentView.addSubview(imageView)
|
||||
if self.hasExpiredSubscription {
|
||||
let imageView = UIImageView(image: UIImage(named: "info-solid-24")?.withRenderingMode(.alwaysTemplate))
|
||||
imageView.tintColor = Theme.accentBlueColor
|
||||
cell.contentView.addSubview(imageView)
|
||||
|
||||
imageView.autoSetDimensions(to: CGSize(square: 24))
|
||||
imageView.autoVCenterInSuperview()
|
||||
imageView.autoPinEdge(.trailing, to: .trailing, of: cell.contentView, withOffset: -8)
|
||||
}
|
||||
imageView.autoSetDimensions(to: CGSize(square: 24))
|
||||
imageView.autoVCenterInSuperview()
|
||||
imageView.autoPinEdge(.trailing, to: .trailing, of: cell.contentView, withOffset: -8)
|
||||
}
|
||||
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
let vc = SubscriptionViewController()
|
||||
self?.navigationController?.pushViewController(vc, animated: true)
|
||||
}))
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
let vc = SubscriptionViewController()
|
||||
self?.navigationController?.pushViewController(vc, animated: true)
|
||||
}))
|
||||
|
||||
section4.add(.disclosureItem(
|
||||
icon: .settingsBoost,
|
||||
name: NSLocalizedString("SETTINGS_ONE_TIME_DONATION", comment: "Title for the 'one time donation' link in settings."),
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "one time donation"),
|
||||
actionBlock: { [weak self] in
|
||||
let vc = BoostViewController()
|
||||
self?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
section4.add(.item(icon: .settingsDonate,
|
||||
tintColor: Theme.primaryIconColor,
|
||||
name: NSLocalizedString("SETTINGS_DONATE", comment: "Title for the 'donate to signal' link in settings."),
|
||||
maxNameLines: 0,
|
||||
textColor: Theme.primaryTextColor,
|
||||
accessoryText: nil,
|
||||
accessoryType: .none,
|
||||
accessoryImage: #imageLiteral(resourceName: "open-20").withRenderingMode(.alwaysTemplate),
|
||||
accessoryView: nil,
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "donate"),
|
||||
actionBlock: { () in
|
||||
UIApplication.shared.open(URL(string: "https://signal.org/donate")!, options: [:], completionHandler: nil)
|
||||
}))
|
||||
}
|
||||
|
||||
} else {
|
||||
section4.add(.disclosureItem(
|
||||
icon: .settingsDonate,
|
||||
name: NSLocalizedString("SETTINGS_DONATE", comment: "Title for the 'donate to signal' link in settings."),
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "donate"),
|
||||
icon: .settingsBoost,
|
||||
name: NSLocalizedString("SETTINGS_ONE_TIME_DONATION", comment: "Title for the 'one time donation' link in settings."),
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "one time donation"),
|
||||
actionBlock: { [weak self] in
|
||||
let vc = DonationViewController()
|
||||
let vc = BoostViewController()
|
||||
self?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
section4.add(.item(icon: .settingsDonate,
|
||||
tintColor: Theme.primaryIconColor,
|
||||
name: NSLocalizedString("SETTINGS_DONATE", comment: "Title for the 'donate to signal' link in settings."),
|
||||
maxNameLines: 0,
|
||||
textColor: Theme.primaryTextColor,
|
||||
accessoryText: nil,
|
||||
accessoryType: .none,
|
||||
accessoryImage: #imageLiteral(resourceName: "open-20").withRenderingMode(.alwaysTemplate),
|
||||
accessoryView: nil,
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "donate"),
|
||||
actionBlock: { () in
|
||||
UIApplication.shared.open(URL(string: "https://signal.org/donate")!, options: [:], completionHandler: nil)
|
||||
}))
|
||||
}
|
||||
|
||||
contents.addSection(section4)
|
||||
|
||||
@ -1,763 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassKit
|
||||
import BonMot
|
||||
import SignalServiceKit
|
||||
|
||||
/* From BonMot 6.0.0: If you're targeting iOS 15 or higher, you may want to check out [AttributedString](https://developer.apple.com/documentation/foundation/attributedstring) instead.
|
||||
If you're an existing user of BonMot using Xcode 13, you may want to add the following `typealias` somewhere in your project to avoid a conflict with `Foundation.StringStyle`: */
|
||||
typealias StringStyle = BonMot.StringStyle
|
||||
|
||||
class DonationViewController: OWSTableViewController2 {
|
||||
private var currencyCode = Stripe.defaultCurrencyCode {
|
||||
didSet {
|
||||
guard oldValue != currencyCode else { return }
|
||||
customValueTextField.setCurrencyCode(currencyCode)
|
||||
state = nil
|
||||
updateTableContents()
|
||||
}
|
||||
}
|
||||
private let customValueTextField = CustomValueTextField()
|
||||
|
||||
private var donationAmount: NSDecimalNumber? {
|
||||
switch state {
|
||||
case .presetSelected(let amount): return NSDecimalNumber(value: amount)
|
||||
case .customValueSelected: return customValueTextField.decimalNumber
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
enum State: Equatable {
|
||||
case presetSelected(amount: UInt)
|
||||
case customValueSelected
|
||||
case donatedSuccessfully
|
||||
}
|
||||
private var state: State? {
|
||||
didSet {
|
||||
guard oldValue != state else { return }
|
||||
if oldValue == .customValueSelected { clearCustomTextField() }
|
||||
if state == .donatedSuccessfully { updateTableContents() }
|
||||
updatePresetButtonSelection()
|
||||
}
|
||||
}
|
||||
|
||||
func clearCustomTextField() {
|
||||
customValueTextField.text = nil
|
||||
customValueTextField.resignFirstResponder()
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
shouldAvoidKeyboard = true
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
customValueTextField.placeholder = NSLocalizedString(
|
||||
"DONATION_VIEW_CUSTOM_AMOUNT_PLACEHOLDER",
|
||||
comment: "Default text for the custom amount field of the donation view."
|
||||
)
|
||||
customValueTextField.delegate = self
|
||||
customValueTextField.accessibilityIdentifier = UIView.accessibilityIdentifier(in: self, name: "custom_amount_text_field")
|
||||
|
||||
updateTableContents()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// If we're the root view, add a cancel button
|
||||
if navigationController?.viewControllers.first == self {
|
||||
navigationItem.leftBarButtonItem = .init(
|
||||
barButtonSystemItem: .done,
|
||||
target: self,
|
||||
action: #selector(didTapDone)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func didTapDone() {
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
static let bubbleBorderWidth: CGFloat = 1.5
|
||||
static let bubbleBorderColor = UIColor(rgbHex: 0xdedede)
|
||||
static var bubbleBackgroundColor: UIColor { Theme.isDarkThemeEnabled ? .ows_gray80 : .ows_white }
|
||||
|
||||
func newCell() -> UITableViewCell {
|
||||
let cell = OWSTableItem.newCell()
|
||||
cell.selectionStyle = .none
|
||||
cell.layoutMargins = cellOuterInsets
|
||||
cell.contentView.layoutMargins = .zero
|
||||
return cell
|
||||
}
|
||||
|
||||
override var canBecomeFirstResponder: Bool { true }
|
||||
override func becomeFirstResponder() -> Bool {
|
||||
let result = super.becomeFirstResponder()
|
||||
// If we become the first responder, but the user was entering
|
||||
// a customValue, restore the first responder state to the text field.
|
||||
if result, case .customValueSelected = state {
|
||||
customValueTextField.becomeFirstResponder()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var presetButtons: [UInt: UIView] = [:]
|
||||
func updatePresetButtonSelection() {
|
||||
for (amount, button) in presetButtons {
|
||||
if case .presetSelected(amount: amount) = self.state {
|
||||
button.layer.borderColor = Theme.accentBlueColor.cgColor
|
||||
} else {
|
||||
button.layer.borderColor = Self.bubbleBorderColor.cgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTableContents() {
|
||||
let contents = OWSTableContents()
|
||||
defer {
|
||||
self.contents = contents
|
||||
if case .customValueSelected = state { customValueTextField.becomeFirstResponder() }
|
||||
}
|
||||
|
||||
let section = OWSTableSection()
|
||||
section.hasBackground = false
|
||||
contents.addSection(section)
|
||||
|
||||
section.customHeaderView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .center
|
||||
stackView.isLayoutMarginsRelativeArrangement = true
|
||||
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 16, bottom: 28, right: 16)
|
||||
|
||||
let imageView = UIImageView()
|
||||
imageView.image = #imageLiteral(resourceName: "character-loving")
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
imageView.autoSetDimension(.height, toSize: 144)
|
||||
stackView.addArrangedSubview(imageView)
|
||||
|
||||
let label = UILabel()
|
||||
label.textAlignment = .center
|
||||
label.font = UIFont.ows_dynamicTypeTitle2.ows_semibold
|
||||
label.text = NSLocalizedString(
|
||||
"DONATION_VIEW_TITLE",
|
||||
comment: "Title for the donate to signal view"
|
||||
)
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
stackView.addArrangedSubview(label)
|
||||
|
||||
return stackView
|
||||
}()
|
||||
|
||||
if case .donatedSuccessfully = state {
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = Self.bubbleBackgroundColor
|
||||
backgroundView.layer.cornerRadius = 12
|
||||
cell.insertSubview(backgroundView, belowSubview: cell.contentView)
|
||||
backgroundView.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
cell.contentView.layoutMargins = UIEdgeInsets(
|
||||
hMargin: Self.cellHInnerMargin,
|
||||
vMargin: 38
|
||||
)
|
||||
|
||||
let label = UILabel()
|
||||
label.textAlignment = .center
|
||||
label.font = .ows_dynamicTypeBody
|
||||
label.textColor = Theme.primaryTextColor
|
||||
label.text = NSLocalizedString(
|
||||
"DONATION_VIEW_THANKS_FOR_YOUR_SUPPORT",
|
||||
comment: "Thank you message on the donate to signal view"
|
||||
)
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
|
||||
cell.contentView.addSubview(label)
|
||||
label.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
} else {
|
||||
addApplePayItemsIfAvailable(to: section)
|
||||
|
||||
// If ApplePay isn't available, show just a link to the website
|
||||
if !DonationUtilities.isApplePayAvailable {
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let donateButton = OWSFlatButton()
|
||||
donateButton.setBackgroundColors(upColor: Theme.accentBlueColor)
|
||||
donateButton.setTitleColor(.ows_white)
|
||||
donateButton.setAttributedTitle(NSAttributedString.composed(of: [
|
||||
NSLocalizedString(
|
||||
"SETTINGS_DONATE",
|
||||
comment: "Title for the 'donate to signal' link in settings."
|
||||
),
|
||||
Special.noBreakSpace,
|
||||
NSAttributedString.with(
|
||||
image: #imageLiteral(resourceName: "open-20").withRenderingMode(.alwaysTemplate),
|
||||
font: UIFont.ows_dynamicTypeBodyClamped.ows_semibold
|
||||
)
|
||||
]).styled(
|
||||
with: .font(UIFont.ows_dynamicTypeBodyClamped.ows_semibold),
|
||||
.color(.ows_white)
|
||||
))
|
||||
donateButton.layer.cornerRadius = 12
|
||||
donateButton.clipsToBounds = true
|
||||
donateButton.setPressedBlock { [weak self] in
|
||||
self?.openDonateWebsite()
|
||||
}
|
||||
|
||||
cell.contentView.addSubview(donateButton)
|
||||
donateButton.autoPinEdgesToSuperviewMargins()
|
||||
donateButton.autoSetDimension(.height, toSize: 48, relation: .greaterThanOrEqual)
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let whySection = OWSTableSection()
|
||||
whySection.hasBackground = false
|
||||
contents.addSection(whySection)
|
||||
|
||||
whySection.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let label = UILabel()
|
||||
label.font = UIFont.ows_dynamicTypeTitle2.ows_semibold
|
||||
label.textColor = Theme.primaryTextColor
|
||||
label.text = NSLocalizedString(
|
||||
"DONATION_VIEW_WHY_DONATE_TITLE",
|
||||
comment: "The title of the 'Why Donate' section of the donate to signal view"
|
||||
)
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
|
||||
cell.contentView.addSubview(label)
|
||||
label.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
|
||||
whySection.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let label = UILabel()
|
||||
label.font = .ows_dynamicTypeBody
|
||||
label.textColor = Theme.primaryTextColor
|
||||
label.text = NSLocalizedString(
|
||||
"DONATION_VIEW_WHY_DONATE_BODY",
|
||||
comment: "The body of the 'Why Donate' section of the donate to signal view"
|
||||
)
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
|
||||
cell.contentView.addSubview(label)
|
||||
label.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
}
|
||||
|
||||
private func openDonateWebsite() {
|
||||
UIApplication.shared.open(URL(string: "https://signal.org/donate")!, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ApplePay
|
||||
|
||||
extension DonationViewController: PKPaymentAuthorizationControllerDelegate {
|
||||
|
||||
func addApplePayItemsIfAvailable(to section: OWSTableSection) {
|
||||
guard DonationUtilities.isApplePayAvailable else { return }
|
||||
|
||||
// Currency Picker
|
||||
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .horizontal
|
||||
stackView.alignment = .center
|
||||
stackView.spacing = 8
|
||||
cell.contentView.addSubview(stackView)
|
||||
stackView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let label = UILabel()
|
||||
label.font = .ows_dynamicTypeBodyClamped
|
||||
label.textColor = Theme.primaryTextColor
|
||||
label.text = NSLocalizedString(
|
||||
"DONATION_VIEW_AMOUNT_LABEL",
|
||||
comment: "Donation amount label for the donate to signal view"
|
||||
)
|
||||
stackView.addArrangedSubview(label)
|
||||
|
||||
let picker = OWSButton { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let vc = CurrencyPickerViewController(
|
||||
dataSource: StripeCurrencyPickerDataSource(currentCurrencyCode: self.currencyCode)
|
||||
) { [weak self] currencyCode in
|
||||
self?.currencyCode = currencyCode
|
||||
}
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
picker.setAttributedTitle(NSAttributedString.composed(of: [
|
||||
self.currencyCode,
|
||||
Special.noBreakSpace,
|
||||
NSAttributedString.with(
|
||||
image: #imageLiteral(resourceName: "chevron-down-18").withRenderingMode(.alwaysTemplate),
|
||||
font: .ows_regularFont(withSize: 17)
|
||||
).styled(
|
||||
with: .color(Self.bubbleBorderColor)
|
||||
)
|
||||
]).styled(
|
||||
with: .font(.ows_regularFont(withSize: 17)),
|
||||
.color(Theme.primaryTextColor)
|
||||
), for: .normal)
|
||||
|
||||
picker.setBackgroundImage(UIImage.init(color: Self.bubbleBackgroundColor), for: .normal)
|
||||
picker.setBackgroundImage(UIImage.init(color: Self.bubbleBackgroundColor.withAlphaComponent(0.8)), for: .highlighted)
|
||||
|
||||
let pillView = PillView()
|
||||
pillView.layer.borderWidth = Self.bubbleBorderWidth
|
||||
pillView.layer.borderColor = Self.bubbleBorderColor.cgColor
|
||||
pillView.clipsToBounds = true
|
||||
pillView.addSubview(picker)
|
||||
picker.autoPinEdgesToSuperviewEdges()
|
||||
picker.autoSetDimension(.width, toSize: 74, relation: .greaterThanOrEqual)
|
||||
|
||||
stackView.addArrangedSubview(pillView)
|
||||
pillView.autoSetDimension(.height, toSize: 36, relation: .greaterThanOrEqual)
|
||||
|
||||
let leadingSpacer = UIView.hStretchingSpacer()
|
||||
let trailingSpacer = UIView.hStretchingSpacer()
|
||||
stackView.insertArrangedSubview(leadingSpacer, at: 0)
|
||||
stackView.addArrangedSubview(trailingSpacer)
|
||||
leadingSpacer.autoMatch(.width, to: .width, of: trailingSpacer)
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
|
||||
// Preset donation options
|
||||
|
||||
if let preset = DonationUtilities.Presets.presets[currencyCode] {
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let vStack = UIStackView()
|
||||
vStack.axis = .vertical
|
||||
vStack.distribution = .fillEqually
|
||||
vStack.spacing = 16
|
||||
cell.contentView.addSubview(vStack)
|
||||
vStack.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
self.presetButtons.removeAll()
|
||||
|
||||
for amounts in preset.amounts.chunked(by: 3) {
|
||||
let hStack = UIStackView()
|
||||
hStack.axis = .horizontal
|
||||
hStack.distribution = .fillEqually
|
||||
hStack.spacing = UIDevice.current.isIPhone5OrShorter ? 8 : 14
|
||||
|
||||
vStack.addArrangedSubview(hStack)
|
||||
|
||||
for amount in amounts {
|
||||
let button = OWSFlatButton()
|
||||
hStack.addArrangedSubview(button)
|
||||
button.setBackgroundColors(
|
||||
upColor: Self.bubbleBackgroundColor,
|
||||
downColor: Self.bubbleBackgroundColor.withAlphaComponent(0.8)
|
||||
)
|
||||
button.layer.cornerRadius = 12
|
||||
button.clipsToBounds = true
|
||||
button.layer.borderWidth = Self.bubbleBorderWidth
|
||||
button.setPressedBlock { [weak self] in
|
||||
self?.state = .presetSelected(amount: amount)
|
||||
}
|
||||
|
||||
button.setTitle(
|
||||
title: DonationUtilities.formatCurrency(NSDecimalNumber(value: amount), currencyCode: self.currencyCode),
|
||||
font: .ows_regularFont(withSize: UIDevice.current.isIPhone5OrShorter ? 18 : 20),
|
||||
titleColor: Theme.primaryTextColor
|
||||
)
|
||||
|
||||
button.autoSetDimension(.height, toSize: 48)
|
||||
|
||||
self.presetButtons[amount] = button
|
||||
}
|
||||
}
|
||||
|
||||
self.updatePresetButtonSelection()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
}
|
||||
|
||||
// Custom donation option
|
||||
|
||||
let applePayButtonIndex = IndexPath(row: section.items.count + 1, section: 0)
|
||||
let customValueTextField = self.customValueTextField
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
customValueTextField.backgroundColor = Self.bubbleBackgroundColor
|
||||
customValueTextField.layer.cornerRadius = 12
|
||||
customValueTextField.layer.borderWidth = Self.bubbleBorderWidth
|
||||
customValueTextField.layer.borderColor = Self.bubbleBorderColor.cgColor
|
||||
|
||||
customValueTextField.font = .ows_dynamicTypeBodyClamped
|
||||
customValueTextField.textColor = Theme.primaryTextColor
|
||||
|
||||
cell.contentView.addSubview(customValueTextField)
|
||||
customValueTextField.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: { [weak self] in
|
||||
customValueTextField.becomeFirstResponder()
|
||||
self?.tableView.scrollToRow(at: applePayButtonIndex, at: .bottom, animated: true)
|
||||
}
|
||||
))
|
||||
|
||||
// Donate with Apple Pay button
|
||||
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let donateButton = PKPaymentButton(
|
||||
paymentButtonType: .donate,
|
||||
paymentButtonStyle: Theme.isDarkThemeEnabled ? .white : .black
|
||||
)
|
||||
donateButton.cornerRadius = 12
|
||||
donateButton.addTarget(self, action: #selector(self.requestApplePayDonation), for: .touchUpInside)
|
||||
cell.contentView.addSubview(donateButton)
|
||||
donateButton.autoPinEdgesToSuperviewMargins()
|
||||
donateButton.autoSetDimension(.height, toSize: 48, relation: .greaterThanOrEqual)
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
|
||||
// Other options button
|
||||
|
||||
section.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = self.newCell()
|
||||
|
||||
let donateButton = OWSFlatButton()
|
||||
donateButton.setTitleColor(Theme.accentBlueColor)
|
||||
donateButton.setAttributedTitle(NSAttributedString.composed(of: [
|
||||
NSLocalizedString(
|
||||
"DONATION_VIEW_OTHER_WAYS",
|
||||
comment: "Text explaining there are other ways to donate on the donation view."
|
||||
),
|
||||
Special.noBreakSpace,
|
||||
NSAttributedString.with(
|
||||
image: #imageLiteral(resourceName: "open-20").withRenderingMode(.alwaysTemplate),
|
||||
font: .ows_dynamicTypeBodyClamped
|
||||
)
|
||||
]).styled(
|
||||
with: .font(.ows_dynamicTypeBodyClamped),
|
||||
.color(Theme.accentBlueColor)
|
||||
))
|
||||
donateButton.setPressedBlock { [weak self] in
|
||||
self?.openDonateWebsite()
|
||||
}
|
||||
|
||||
cell.contentView.addSubview(donateButton)
|
||||
donateButton.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
return cell
|
||||
},
|
||||
actionBlock: {}
|
||||
))
|
||||
}
|
||||
|
||||
@objc
|
||||
func requestApplePayDonation() {
|
||||
guard let donationAmount = donationAmount else {
|
||||
presentToast(text: NSLocalizedString(
|
||||
"DONATION_VIEW_SELECT_AN_AMOUNT",
|
||||
comment: "Error text notifying the user they must select an amount on the donate to signal view"
|
||||
), extraVInset: view.height - tableView.frame.maxY)
|
||||
return
|
||||
}
|
||||
|
||||
guard !Stripe.isAmountTooSmall(donationAmount, in: currencyCode) else {
|
||||
presentToast(text: NSLocalizedString(
|
||||
"DONATION_VIEW_SELECT_A_LARGER_AMOUNT",
|
||||
comment: "Error text notifying the user they must select a large amount on the donate to signal view"
|
||||
), extraVInset: view.height - tableView.frame.maxY)
|
||||
return
|
||||
}
|
||||
|
||||
guard !Stripe.isAmountTooLarge(donationAmount, in: currencyCode) else {
|
||||
presentToast(text: NSLocalizedString(
|
||||
"DONATION_VIEW_SELECT_A_SMALLER_AMOUNT",
|
||||
comment: "Error text notifying the user they must select a smaller amount on the donate to signal view"
|
||||
), extraVInset: view.height - tableView.frame.maxY)
|
||||
return
|
||||
}
|
||||
|
||||
let request = DonationUtilities.newPaymentRequest(for: donationAmount, currencyCode: currencyCode)
|
||||
|
||||
let paymentController = PKPaymentAuthorizationController(paymentRequest: request)
|
||||
paymentController.delegate = self
|
||||
paymentController.present { presented in
|
||||
if !presented { owsFailDebug("Failed to present payment controller") }
|
||||
}
|
||||
}
|
||||
|
||||
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
|
||||
controller.dismiss()
|
||||
}
|
||||
|
||||
func paymentAuthorizationController(
|
||||
_ controller: PKPaymentAuthorizationController,
|
||||
didAuthorizePayment payment: PKPayment,
|
||||
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
|
||||
) {
|
||||
guard let donationAmount = donationAmount else {
|
||||
completion(.init(status: .failure, errors: [OWSAssertionError("Missing donation amount")]))
|
||||
return
|
||||
}
|
||||
Stripe.donate(amount: donationAmount, in: currencyCode, for: payment).done { [weak self] in
|
||||
completion(.init(status: .success, errors: nil))
|
||||
self?.state = .donatedSuccessfully
|
||||
}.catch { error in
|
||||
owsFailDebugUnlessNetworkFailure(error)
|
||||
completion(.init(status: .failure, errors: [error]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomValueTextField
|
||||
|
||||
private protocol CustomValueTextFieldDelegate: AnyObject {
|
||||
func customValueTextFieldStateDidChange(_ textField: CustomValueTextField)
|
||||
}
|
||||
|
||||
private class CustomValueTextField: UIView {
|
||||
private let placeholderLabel = UILabel()
|
||||
private let symbolLabel = UILabel()
|
||||
private let textField = UITextField()
|
||||
private let stackView = UIStackView()
|
||||
|
||||
weak var delegate: CustomValueTextFieldDelegate?
|
||||
|
||||
@discardableResult
|
||||
override func becomeFirstResponder() -> Bool { textField.becomeFirstResponder() }
|
||||
|
||||
@discardableResult
|
||||
override func resignFirstResponder() -> Bool { textField.resignFirstResponder() }
|
||||
|
||||
override var canBecomeFirstResponder: Bool { textField.canBecomeFirstResponder }
|
||||
override var canResignFirstResponder: Bool { textField.canResignFirstResponder }
|
||||
override var isFirstResponder: Bool { textField.isFirstResponder }
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
textField.autocorrectionType = .no
|
||||
textField.spellCheckingType = .no
|
||||
textField.keyboardType = .decimalPad
|
||||
textField.textAlignment = .center
|
||||
textField.delegate = self
|
||||
|
||||
symbolLabel.textAlignment = .center
|
||||
placeholderLabel.textAlignment = .center
|
||||
|
||||
stackView.axis = .horizontal
|
||||
|
||||
stackView.addArrangedSubview(placeholderLabel)
|
||||
stackView.addArrangedSubview(textField)
|
||||
|
||||
addSubview(stackView)
|
||||
stackView.autoPinHeightToSuperview()
|
||||
stackView.autoMatch(.width, to: .width, of: self, withMultiplier: 1, relation: .lessThanOrEqual)
|
||||
stackView.autoHCenterInSuperview()
|
||||
stackView.autoSetDimension(.height, toSize: 48, relation: .greaterThanOrEqual)
|
||||
|
||||
updateVisibility()
|
||||
setCurrencyCode(currencyCode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
var text: String? {
|
||||
set {
|
||||
textField.text = newValue
|
||||
updateVisibility()
|
||||
}
|
||||
get { textField.text }
|
||||
}
|
||||
|
||||
var decimalNumber: NSDecimalNumber? {
|
||||
let number = NSDecimalNumber(string: valueString(for: text), locale: Locale.current)
|
||||
guard number != NSDecimalNumber.notANumber else { return nil }
|
||||
return number
|
||||
}
|
||||
|
||||
var font: UIFont? {
|
||||
set {
|
||||
textField.font = newValue
|
||||
placeholderLabel.font = newValue
|
||||
symbolLabel.font = newValue
|
||||
}
|
||||
get { textField.font }
|
||||
}
|
||||
|
||||
var textColor: UIColor? {
|
||||
set {
|
||||
textField.textColor = newValue
|
||||
placeholderLabel.textColor = newValue
|
||||
symbolLabel.textColor = newValue
|
||||
}
|
||||
get { textField.textColor }
|
||||
}
|
||||
|
||||
var placeholder: String? {
|
||||
set { placeholderLabel.text = newValue }
|
||||
get { placeholderLabel.text }
|
||||
}
|
||||
|
||||
private lazy var symbol: DonationUtilities.Symbol = DonationUtilities.Presets.presets[currencyCode]?.symbol ?? .currencyCode
|
||||
private lazy var currencyCode = Stripe.defaultCurrencyCode
|
||||
|
||||
func setCurrencyCode(_ currencyCode: Currency.Code) {
|
||||
self.symbol = DonationUtilities.Presets.symbol(for: currencyCode)
|
||||
self.currencyCode = currencyCode
|
||||
|
||||
symbolLabel.removeFromSuperview()
|
||||
|
||||
switch symbol {
|
||||
case .before(let symbol):
|
||||
symbolLabel.text = symbol
|
||||
stackView.insertArrangedSubview(symbolLabel, at: 0)
|
||||
case .after(let symbol):
|
||||
symbolLabel.text = symbol
|
||||
stackView.addArrangedSubview(symbolLabel)
|
||||
case .currencyCode:
|
||||
symbolLabel.text = currencyCode + " "
|
||||
stackView.insertArrangedSubview(symbolLabel, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
func updateVisibility() {
|
||||
let shouldShowPlaceholder = text.isEmptyOrNil && !isFirstResponder
|
||||
placeholderLabel.isHiddenInStackView = !shouldShowPlaceholder
|
||||
symbolLabel.isHiddenInStackView = shouldShowPlaceholder
|
||||
textField.isHiddenInStackView = shouldShowPlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
extension CustomValueTextField: UITextFieldDelegate {
|
||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
updateVisibility()
|
||||
delegate?.customValueTextFieldStateDidChange(self)
|
||||
}
|
||||
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
updateVisibility()
|
||||
}
|
||||
|
||||
func textField(_ textField: UITextField, shouldChangeCharactersIn editingRange: NSRange, replacementString: String) -> Bool {
|
||||
let existingString = textField.text ?? ""
|
||||
|
||||
let newString = (existingString as NSString).replacingCharacters(in: editingRange, with: replacementString)
|
||||
if let numberString = self.valueString(for: newString) {
|
||||
textField.text = numberString
|
||||
// Make a best effort to preserve cursor position
|
||||
if let newPosition = textField.position(
|
||||
from: textField.beginningOfDocument,
|
||||
offset: editingRange.location + max(0, numberString.count - existingString.count)
|
||||
) {
|
||||
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
|
||||
}
|
||||
} else {
|
||||
textField.text = ""
|
||||
}
|
||||
|
||||
updateVisibility()
|
||||
delegate?.customValueTextFieldStateDidChange(self)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/// Converts an arbitrary string into a string representing a valid value
|
||||
/// for the current currency. If no valid value is represented, returns nil
|
||||
func valueString(for string: String?) -> String? {
|
||||
guard let string = string else { return nil }
|
||||
|
||||
let isZeroDecimalCurrency = Stripe.zeroDecimalCurrencyCodes.contains(currencyCode)
|
||||
guard !isZeroDecimalCurrency else { return string.digitsOnly }
|
||||
|
||||
let decimalSeparator = Locale.current.decimalSeparator ?? "."
|
||||
let components = string.components(separatedBy: decimalSeparator).compactMap { $0.digitsOnly.nilIfEmpty }
|
||||
|
||||
guard let integralString = components.first else {
|
||||
if string.contains(decimalSeparator) {
|
||||
return "0" + decimalSeparator
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if let decimalString = components.dropFirst().joined().nilIfEmpty {
|
||||
return integralString + decimalSeparator + decimalString
|
||||
} else if string.starts(with: decimalSeparator) {
|
||||
return "0" + decimalSeparator + integralString
|
||||
} else if string.contains(decimalSeparator) {
|
||||
return integralString + decimalSeparator
|
||||
} else {
|
||||
return integralString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DonationViewController: CustomValueTextFieldDelegate {
|
||||
fileprivate func customValueTextFieldStateDidChange(_ textField: CustomValueTextField) {
|
||||
state = .customValueSelected
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,10 @@
|
||||
import Foundation
|
||||
import BonMot
|
||||
|
||||
/* From BonMot 6.0.0: If you're targeting iOS 15 or higher, you may want to check out [AttributedString](https://developer.apple.com/documentation/foundation/attributedstring) instead.
|
||||
If you're an existing user of BonMot using Xcode 13, you may want to add the following `typealias` somewhere in your project to avoid a conflict with `Foundation.StringStyle`: */
|
||||
typealias StringStyle = BonMot.StringStyle
|
||||
|
||||
@objc
|
||||
public protocol ConversationSearchViewDelegate: AnyObject {
|
||||
func conversationSearchViewWillBeginDragging()
|
||||
|
||||
@ -1639,39 +1639,9 @@
|
||||
/* Text at the bottom of the donation receipts list, telling users that receipts might not be available */
|
||||
"DONATION_RECEIPTS_MIGHT_BE_MISSING_IF_YOU_REINSTALLED" = "If you have reinstalled Signal, receipts from previous donations will not be available.";
|
||||
|
||||
/* Donation amount label for the donate to signal view */
|
||||
"DONATION_VIEW_AMOUNT_LABEL" = "Donation amount";
|
||||
|
||||
/* Default text for the custom amount field of the donation view. */
|
||||
"DONATION_VIEW_CUSTOM_AMOUNT_PLACEHOLDER" = "Enter Custom Amount";
|
||||
|
||||
/* Text describing to the user that they're going to pay a donation to Signal */
|
||||
"DONATION_VIEW_DONATION_TO_SIGNAL" = "Donation to Signal";
|
||||
|
||||
/* Text explaining there are other ways to donate on the donation view. */
|
||||
"DONATION_VIEW_OTHER_WAYS" = "Other Ways to Donate";
|
||||
|
||||
/* Error text notifying the user they must select a large amount on the donate to signal view */
|
||||
"DONATION_VIEW_SELECT_A_LARGER_AMOUNT" = "Select a larger amount to donate";
|
||||
|
||||
/* Error text notifying the user they must select a smaller amount on the donate to signal view */
|
||||
"DONATION_VIEW_SELECT_A_SMALLER_AMOUNT" = "Select a smaller amount to donate";
|
||||
|
||||
/* Error text notifying the user they must select an amount on the donate to signal view */
|
||||
"DONATION_VIEW_SELECT_AN_AMOUNT" = "Select an amount to donate";
|
||||
|
||||
/* Thank you message on the donate to signal view */
|
||||
"DONATION_VIEW_THANKS_FOR_YOUR_SUPPORT" = "Thank you for your support!";
|
||||
|
||||
/* Title for the donate to signal view */
|
||||
"DONATION_VIEW_TITLE" = "Signal is powered by people like you.";
|
||||
|
||||
/* The body of the 'Why Donate' section of the donate to signal view */
|
||||
"DONATION_VIEW_WHY_DONATE_BODY" = "The team at Signal is committed to the mission of developing open source privacy technology that protects free expression and enables secure global communication. Your contribution fuels this cause. No ads. No trackers. No kidding.\n\nYour donation helps pay for the development, servers, and bandwidth of an app used by millions around the world for private and instantaneous communication.";
|
||||
|
||||
/* The title of the 'Why Donate' section of the donate to signal view */
|
||||
"DONATION_VIEW_WHY_DONATE_TITLE" = "Why Donate?";
|
||||
|
||||
/* Alert body for when the user has just tried to edit a contacts after declining to give Signal contacts permissions */
|
||||
"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_BODY" = "You can enable access in the iOS Settings app.";
|
||||
|
||||
|
||||
@ -381,9 +381,6 @@ public class DebugFlags: BaseFlags {
|
||||
@objc
|
||||
public static let forceDonorBadgeDisplay = build.includes(.qa)
|
||||
|
||||
@objc
|
||||
public static let forceDonorBadgeAcquisition = build.includes(.qa)
|
||||
|
||||
@objc
|
||||
public static let forceSubscriptionMegaphone = build.includes(.qa)
|
||||
|
||||
|
||||
@ -202,11 +202,6 @@ public class RemoteConfig: BaseFlags {
|
||||
DebugFlags.forceDonorBadgeDisplay || !isEnabled(.donorBadgeDisplayKillSwitch)
|
||||
}
|
||||
|
||||
@objc
|
||||
public static var donorBadgeAcquisition: Bool {
|
||||
DebugFlags.forceDonorBadgeAcquisition || !isEnabled(.donorBadgeAcquisitionKillSwitch)
|
||||
}
|
||||
|
||||
@objc
|
||||
public static var changePhoneNumberUI: Bool {
|
||||
if DebugFlags.forceChangePhoneNumberUI.get() {
|
||||
@ -383,7 +378,8 @@ private struct Flags {
|
||||
// as soon as we fetch an update to the remote config. They will not
|
||||
// wait for an app restart.
|
||||
enum HotSwappableIsEnabledFlags: String, FlagType {
|
||||
case donorBadgeAcquisitionKillSwitch
|
||||
// This can't be empty, so we define a bogus case. Remove this if you add a flag here.
|
||||
case __noHotSwappableIsEnabledFlags
|
||||
}
|
||||
|
||||
// We filter the received config down to just the supported flags.
|
||||
@ -400,7 +396,6 @@ private struct Flags {
|
||||
case senderKeyKillSwitch
|
||||
case messageResendKillSwitch
|
||||
case donorBadgeDisplayKillSwitch
|
||||
case donorBadgeAcquisitionKillSwitch
|
||||
case changePhoneNumberUI
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user