Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Fix #3374: Add opt-in to Brave Today
Browse files Browse the repository at this point in the history
  • Loading branch information
kylehickinson committed Mar 15, 2021
1 parent adfdb2c commit 5ed9a2d
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 51 deletions.
22 changes: 20 additions & 2 deletions BraveShared/BraveStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,12 @@ extension Strings {
value: "Sources & Settings",
comment: ""
)
public static let turnOnBraveToday = NSLocalizedString(
"today.turnOnBraveToday",
bundle: .braveShared,
value: "Turn on Brave Today",
comment: ""
)
public static let learnMoreTitle = NSLocalizedString(
"today.learnMoreTitle",
bundle: .braveShared,
Expand All @@ -1607,13 +1613,25 @@ extension Strings {
public static let introCardTitle = NSLocalizedString(
"today.introCardTitle",
bundle: .braveShared,
value: "Brave Today shows top stories in a completely private feed, just for you.",
value: "Today's top stories in a completely private feed, just for you.",
comment: ""
)
public static let introCardBody = NSLocalizedString(
"today.introCardBody",
bundle: .braveShared,
value: "Your interests are matched on your device so your personal information never leaves your browser. New content updated throughout the day.",
value: "Brave Today is ad-supported with completely private and anonymized ads matched on your device.",
comment: ""
)
public static let introCardNew = NSLocalizedString(
"today.introCardNew",
bundle: .braveShared,
value: "New",
comment: "\"New\" as in a new feature being introduced, displayed above a body of text explaining said feature"
)
public static let introCardNewTextBody = NSLocalizedString(
"today.introCardNewTextBody",
bundle: .braveShared,
value: "Customize Brave Today with up to 5 RSS feeds and get new content from your favorite publishers throughout the day.",
comment: ""
)
public static let refresh = NSLocalizedString(
Expand Down
3 changes: 2 additions & 1 deletion BraveShared/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ extension Preferences {
}

public final class BraveToday {
public static let isShowingOptIn = Option<Bool>(key: "brave-today.showing-opt-in", default: false)
public static let userOptedIn = Option<Bool>(key: "brave-today.user-opted-in", default: false)
public static let isEnabled = Option<Bool>(key: "brave-today.enabled", default: true)
public static let languageChecked = Option<Bool>(key: "brave-today.language-checked", default: false)
public static let isShowingIntroCard = Option<Bool>(key: "brave-today.showing-intro-card", default: true)
public static let debugEnvironment = Option<String?>(key: "brave-today.debug.environment", default: nil)
}

Expand Down
10 changes: 10 additions & 0 deletions BraveUI/Buttons/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ open class Button: UIButton {
}
}

public override var isHighlighted: Bool {
didSet {
if buttonType == .system { return }
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1.0) {
self.alpha = self.isHighlighted ? 0.6 : 1.0
}
.startAnimation()
}
}

// MARK: - Image Placement

open var flipImageOrigin: Bool = false
Expand Down
9 changes: 5 additions & 4 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
27201EFE24539B5500C19DD1 /* NewTabPageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27201EFD24539B5500C19DD1 /* NewTabPageBackground.swift */; };
27201F022458879700C19DD1 /* NewTabPageBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27201F012458879700C19DD1 /* NewTabPageBackgroundView.swift */; };
27201F0424589B9800C19DD1 /* NewTabPageNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27201F0324589B9800C19DD1 /* NewTabPageNotifications.swift */; };
2726636924944BFA0056CFE1 /* BraveTodayWelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2726636824944BFA0056CFE1 /* BraveTodayWelcomeView.swift */; };
2726636924944BFA0056CFE1 /* BraveTodayOptInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2726636824944BFA0056CFE1 /* BraveTodayOptInView.swift */; };
2726637324981B600056CFE1 /* FeedSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2726637224981B5F0056CFE1 /* FeedSectionHeaderView.swift */; };
2727369B24A65F650096DCB9 /* UIActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2727369A24A65F650096DCB9 /* UIActionExtensions.swift */; };
27384CAE254360120086922F /* OnboardingRewardsAgreementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1D8C6C232BF9C200BDE662 /* OnboardingRewardsAgreementView.swift */; };
Expand Down Expand Up @@ -1619,7 +1619,7 @@
27201EFD24539B5500C19DD1 /* NewTabPageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageBackground.swift; sourceTree = "<group>"; };
27201F012458879700C19DD1 /* NewTabPageBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageBackgroundView.swift; sourceTree = "<group>"; };
27201F0324589B9800C19DD1 /* NewTabPageNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageNotifications.swift; sourceTree = "<group>"; };
2726636824944BFA0056CFE1 /* BraveTodayWelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveTodayWelcomeView.swift; sourceTree = "<group>"; };
2726636824944BFA0056CFE1 /* BraveTodayOptInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveTodayOptInView.swift; sourceTree = "<group>"; };
2726637224981B5F0056CFE1 /* FeedSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedSectionHeaderView.swift; sourceTree = "<group>"; };
2727369A24A65F650096DCB9 /* UIActionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActionExtensions.swift; sourceTree = "<group>"; };
273FCB9925A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveTodayDebugSettingsController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3807,7 +3807,7 @@
27A1ABF924855C1700344503 /* Cards */ = {
isa = PBXGroup;
children = (
2726636824944BFA0056CFE1 /* BraveTodayWelcomeView.swift */,
2726636824944BFA0056CFE1 /* BraveTodayOptInView.swift */,
27E0652724CB6AE300134946 /* BraveTodayErrorView.swift */,
2784874424DC658C0004F03C /* BraveTodayEmptyFeedView.swift */,
27A1ABFC24855C1700344503 /* FeedCardBackgroundView.swift */,
Expand Down Expand Up @@ -7133,7 +7133,8 @@
44331DD8225521B6007E3E93 /* UrlBarTextField.swift in Sources */,
A9072B801D07B34100459960 /* NoImageModeHelper.swift in Sources */,
4422D4EB21BFFB7600BF1855 /* filename.cc in Sources */,
2726636924944BFA0056CFE1 /* BraveTodayWelcomeView.swift in Sources */,
2726636924944BFA0056CFE1 /* BraveTodayOptInView.swift in Sources */,
2FE5B4542582BEF500BFDDB8 /* ShareTrayView.swift in Sources */,
2755EABD255323C60033C43F /* PublisherInfoExtensions.swift in Sources */,
2746D28324A4FB7400E38852 /* RewardsInternalsSharable.swift in Sources */,
44331DDA22552313007E3E93 /* LocationContainerView.swift in Sources */,
Expand Down
10 changes: 9 additions & 1 deletion Client/Application/Delegates/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UIViewControllerRestorati
}

#if !NO_BRAVE_TODAY
if Preferences.BraveToday.isEnabled.value && !Preferences.BraveToday.userOptedIn.value {
// Opt-out any user that has not explicitly opted-in
Preferences.BraveToday.isEnabled.value = false
// User now has to explicitly opt-in
Preferences.BraveToday.isShowingOptIn.value = true
}

if !Preferences.BraveToday.languageChecked.value,
let languageCode = Locale.preferredLanguages.first?.prefix(2) {
Preferences.BraveToday.languageChecked.value = true
Preferences.BraveToday.isEnabled.value = FeedDataSource.supportedLanguages.contains(String(languageCode))
// Base opt-in visibility on whether or not the user's language is supported in BT
Preferences.BraveToday.isShowingOptIn.value = FeedDataSource.supportedLanguages.contains(String(languageCode))
}
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import Lottie
import BraveUI
import Shared

enum WelcomeCardAction {
enum OptInCardAction {
case closedButtonTapped
case settingsButtonTapped
case turnOnBraveTodayButtonTapped
case learnMoreButtonTapped
}

class BraveTodayWelcomeView: UIView, FeedCardContent {
class BraveTodayOptInView: UIView, FeedCardContent {
private let backgroundView = FeedCardBackgroundView()

private let stackView = UIStackView().then {
Expand All @@ -28,19 +28,22 @@ class BraveTodayWelcomeView: UIView, FeedCardContent {
$0.loopMode = .loop
}

var introCardActionHandler: ((WelcomeCardAction) -> Void)?
var optInCardActionHandler: ((OptInCardAction) -> Void)?

private let closeButton = UIButton(type: .system).then {
$0.setImage(#imageLiteral(resourceName: "card_close").withRenderingMode(.alwaysOriginal), for: .normal)
$0.accessibilityLabel = Strings.close
}

private let sourcesAndSettingsButton = ActionButton(type: .system).then {
let turnOnBraveTodayButton = ActionButton().then {
$0.layer.borderWidth = 0
$0.titleLabel?.font = .systemFont(ofSize: 16.0, weight: .semibold)
$0.setTitle(Strings.BraveToday.sourcesAndSettings, for: .normal)
$0.setTitle(Strings.BraveToday.turnOnBraveToday, for: .normal)
$0.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
$0.backgroundColor = UIColor.white.withAlphaComponent(0.2)
$0.backgroundColor = Colors.blurple400
$0.loaderView = LoaderView(size: .small).then {
$0.tintColor = .white
}
}

private let learnMoreButton = UIButton(type: .system).then {
Expand Down Expand Up @@ -80,8 +83,23 @@ class BraveTodayWelcomeView: UIView, FeedCardContent {
$0.font = .systemFont(ofSize: 14)
$0.numberOfLines = 0
}),
.view(UIStackView().then {
$0.spacing = 4
$0.axis = .vertical
$0.alignment = .center
$0.addStackViewItems(
.view(MaskedNewLabel()),
.view(UILabel().then {
$0.text = Strings.BraveToday.introCardNewTextBody
$0.textAlignment = .center
$0.appearanceTextColor = .white
$0.font = .systemFont(ofSize: 13)
$0.numberOfLines = 0
})
)
}),
.customSpace(24),
.view(sourcesAndSettingsButton),
.view(turnOnBraveTodayButton),
.view(learnMoreButton)
)

Expand All @@ -91,21 +109,21 @@ class BraveTodayWelcomeView: UIView, FeedCardContent {

closeButton.addTarget(self, action: #selector(tappedCloseButton), for: .touchUpInside)
learnMoreButton.addTarget(self, action: #selector(tappedLearnMoreButton), for: .touchUpInside)
sourcesAndSettingsButton.addTarget(self, action: #selector(tappedSettingsButton), for: .touchUpInside)
turnOnBraveTodayButton.addTarget(self, action: #selector(tappedTurnOnBraveButton), for: .touchUpInside)
}

// MARK: - Actions

@objc private func tappedCloseButton() {
introCardActionHandler?(.closedButtonTapped)
optInCardActionHandler?(.closedButtonTapped)
}

@objc private func tappedLearnMoreButton() {
introCardActionHandler?(.learnMoreButtonTapped)
optInCardActionHandler?(.learnMoreButtonTapped)
}

@objc private func tappedSettingsButton() {
introCardActionHandler?(.settingsButtonTapped)
@objc private func tappedTurnOnBraveButton() {
optInCardActionHandler?(.turnOnBraveTodayButtonTapped)
}

@available(*, unavailable)
Expand All @@ -126,3 +144,51 @@ class BraveTodayWelcomeView: UIView, FeedCardContent {
}
}
}

/// Displays the word "New" with a gradient mask
private class MaskedNewLabel: UIView {
private let gradientView = GradientView(
colors: [UIColor(rgb: 0x4C54D2), UIColor(rgb: 0xBF14A2), UIColor(rgb: 0xF73A1C)], // light
positions: [0, 0.4, 1],
startPoint: .zero,
endPoint: .init(x: 1, y: 1)
)
private let label = UILabel().then {
$0.text = Strings.BraveToday.introCardNew.uppercased()
$0.appearanceTextColor = .black
$0.font = .systemFont(ofSize: 12, weight: .bold)
}
override init(frame: CGRect) {
super.init(frame: frame)

backgroundColor = .white
layer.cornerRadius = 8
if #available(iOS 13.0, *) {
layer.cornerCurve = .continuous
}

gradientView.mask = label
clipsToBounds = true

addSubview(gradientView)
label.sizeToFit()
gradientView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(UIEdgeInsets(top: 3, left: 6, bottom: 3, right: 6))
$0.size.equalTo(label.bounds.size)
}
isAccessibilityElement = true
accessibilityTraits = [.staticText]
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame = gradientView.bounds
}
@available(*, unavailable)
required init(coder: NSCoder) {
fatalError()
}
override var accessibilityLabel: String? {
get { label.accessibilityLabel }
set { } // swiftlint:disable:this unused_setter_value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class NewTabPageBackgroundButtonsView: UIView, PreferencesObserver {
#else
let braveTodayVisible =
!PrivateBrowsingManager.shared.isPrivateBrowsing &&
Preferences.BraveToday.isEnabled.value
(Preferences.BraveToday.isEnabled.value || Preferences.BraveToday.isShowingOptIn.value)
#endif

imageCreditButton.snp.remakeConstraints {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class NewTabPageViewController: UIViewController, Themeable {

private let feedDataSource: FeedDataSource
private let feedOverlayView = NewTabPageFeedOverlayView()
private var preventReloadOnBraveTodayEnabledChange = false

private let notifications: NewTabPageNotifications

Expand Down Expand Up @@ -479,15 +480,26 @@ class NewTabPageViewController: UIViewController, Themeable {

private func handleBraveTodayAction(_ action: BraveTodaySectionProvider.Action) {
switch action {
case .welcomeCardAction(.closedButtonTapped):
Preferences.BraveToday.isShowingIntroCard.value = false
case .optInCardAction(.closedButtonTapped):
Preferences.BraveToday.isShowingOptIn.value = false
if let section = layout.braveTodaySection, collectionView.numberOfItems(inSection: section) != 0 {
collectionView.deleteItems(at: [IndexPath(item: 0, section: section)])
}
case .welcomeCardAction(.learnMoreButtonTapped):
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .top, animated: true)
collectionView.verticalScrollIndicatorInsets = .zero
UIView.animate(withDuration: 0.25) {
self.feedOverlayView.headerView.alpha = 0.0
self.backgroundButtonsView.alpha = 1.0
}
case .optInCardAction(.learnMoreButtonTapped):
delegate?.navigateToInput(BraveUX.braveTodayPrivacyURL.absoluteString, inNewTab: false, switchingToPrivateMode: false)
case .welcomeCardAction(.settingsButtonTapped),
.emptyCardTappedSourcesAndSettings:
case .optInCardAction(.turnOnBraveTodayButtonTapped):
preventReloadOnBraveTodayEnabledChange = true
Preferences.BraveToday.userOptedIn.value = true
Preferences.BraveToday.isShowingOptIn.value = false
Preferences.BraveToday.isEnabled.value = true
loadFeedContents()
case .emptyCardTappedSourcesAndSettings:
tappedBraveTodaySettings()
case .errorCardTappedRefresh:
loadFeedContents()
Expand Down Expand Up @@ -619,7 +631,7 @@ class NewTabPageViewController: UIViewController, Themeable {

@objc private func checkForUpdatedFeed() {
#if !NO_BRAVE_TODAY
if !isBraveTodayVisible { return }
if !isBraveTodayVisible || Preferences.BraveToday.isShowingOptIn.value { return }
if collectionView.contentOffset.y == collectionView.contentInset.top {
// Reload contents if the user is not currently scrolled into the feed
loadFeedContents()
Expand Down Expand Up @@ -738,12 +750,15 @@ class NewTabPageViewController: UIViewController, Themeable {

extension NewTabPageViewController: PreferencesObserver {
func preferencesDidChange(for key: String) {
collectionView.reloadData()
if !preventReloadOnBraveTodayEnabledChange {
collectionView.reloadData()
}
if !isBraveTodayVisible {
collectionView.verticalScrollIndicatorInsets = .zero
feedOverlayView.headerView.alpha = 0.0
backgroundButtonsView.alpha = 1.0
}
preventReloadOnBraveTodayEnabledChange = false
}
}

Expand All @@ -754,7 +769,7 @@ extension NewTabPageViewController {
return false
#else
return !PrivateBrowsingManager.shared.isPrivateBrowsing &&
Preferences.BraveToday.isEnabled.value
(Preferences.BraveToday.isEnabled.value || Preferences.BraveToday.isShowingOptIn.value)
#endif
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
Expand Down
Loading

0 comments on commit 5ed9a2d

Please sign in to comment.