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

Commit

Permalink
No Bug: Add Brave Today debug menu/environment switcher (#3182)
Browse files Browse the repository at this point in the history
  • Loading branch information
kylehickinson authored Jan 12, 2021
1 parent 827b27b commit f596798
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 11 deletions.
1 change: 1 addition & 0 deletions BraveShared/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extension Preferences {
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)
}

public final class Review {
Expand Down
4 changes: 4 additions & 0 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@
272FCAA0225CF8F00091E645 /* OnePasswordExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 272FCA98225CF8F00091E645 /* OnePasswordExtension.framework */; };
27384CAE254360120086922F /* OnboardingRewardsAgreementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1D8C6C232BF9C200BDE662 /* OnboardingRewardsAgreementView.swift */; };
273EB3A72422AB24002A8AAF /* PaymentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9616E6A240EE43F00667C2D /* PaymentRequest.swift */; };
273FCB9A25A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 273FCB9925A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift */; };
274398E224E4827800E79605 /* FeedCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274398E124E4827800E79605 /* FeedCard.swift */; };
274398E524E4829900E79605 /* FeedFillStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274398E424E4829900E79605 /* FeedFillStrategy.swift */; };
274398E724E483AD00E79605 /* FeedItemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274398E624E483AD00E79605 /* FeedItemMenu.swift */; };
Expand Down Expand Up @@ -1512,6 +1513,7 @@
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>"; };
272FCA98225CF8F00091E645 /* OnePasswordExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OnePasswordExtension.framework; path = Carthage/Build/iOS/OnePasswordExtension.framework; sourceTree = "<group>"; };
273FCB9925A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveTodayDebugSettingsController.swift; sourceTree = "<group>"; };
274398E124E4827800E79605 /* FeedCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCard.swift; sourceTree = "<group>"; };
274398E424E4829900E79605 /* FeedFillStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedFillStrategy.swift; sourceTree = "<group>"; };
274398E624E483AD00E79605 /* FeedItemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedItemMenu.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3426,6 +3428,7 @@
27D114D32358FBBF00166534 /* BraveRewardsSettingsViewController.swift */,
27D114D52358FCA400166534 /* SettingsRowViews.swift */,
27D67CEC24D07EB800066D83 /* BraveTodaySettingsViewController.swift */,
273FCB9925A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift */,
278C700924F96D7000A246C8 /* BraveShieldsAndPrivacySettingsController.swift */,
);
path = Settings;
Expand Down Expand Up @@ -6215,6 +6218,7 @@
E63ED8E11BFD25580097D08E /* LoginListViewController.swift in Sources */,
D0625CA8208FC47A0081F3B2 /* BrowserViewController+DownloadQueueDelegate.swift in Sources */,
2F44FCC71A9E8CF500FD20CC /* SearchSettingsTableViewController.swift in Sources */,
273FCB9A25A7BC5500F279B5 /* BraveTodayDebugSettingsController.swift in Sources */,
39A359E41BFCCE94006B9E87 /* UserActivityHandler.swift in Sources */,
0AF6B1EA24A0EE19005417FC /* InstallVPNView.swift in Sources */,
27201F0424589B9800C19DD1 /* NewTabPageNotifications.swift in Sources */,
Expand Down
50 changes: 39 additions & 11 deletions Client/Frontend/Brave Today/Composer/FeedDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ class FeedDataSource {

init() {
restoreCachedSources()
if !AppConstants.buildChannel.isPublic,
let savedEnvironment = Preferences.BraveToday.debugEnvironment.value,
let environment = Environment(rawValue: savedEnvironment) {
self.environment = environment
}
}

// MARK: - Resource Managment
Expand All @@ -85,19 +90,39 @@ class FeedDataSource {
return decoder
}()

/// A Brave Today environment
enum Environment: String, CaseIterable {
case dev = "brave.software"
case staging = "bravesoftware.com"
case production = "brave.com"
}

/// The current Brave Today environment.
///
/// Updating the environment automatically clears the current cached items if any exist.
///
/// - warning: Should only be changed in non-public releases
var environment: Environment = .production {
didSet {
if oldValue == environment { return }
assert(!AppConstants.buildChannel.isPublic,
"Environment cannot be changed on non-public build channels")
Preferences.BraveToday.debugEnvironment.value = environment.rawValue
clearCachedFiles()
}
}

private struct TodayBucket {
var name: String
var path: String = ""

var url: URL {
var components = URLComponents()
components.scheme = "https"
// TODO: At the moment these files are only available on the dev servers, eventually we will
// change `brave.software` to `bravesoftware.com` or `brave.com` based on staging/prod
components.host = "\(name).brave.com"
components.path = "/\(path)"
return components.url!
}
}

private func resourceUrl(for bucket: TodayBucket) -> URL? {
var components = URLComponents()
components.scheme = "https"
components.host = "\(bucket.name).\(environment.rawValue)"
components.path = "/\(bucket.path)"
return components.url
}

private struct TodayResource {
Expand Down Expand Up @@ -180,8 +205,11 @@ class FeedDataSource {
if let data = data {
return .init(value: .success(data), defaultQueue: .main)
}
guard let url = self.resourceUrl(for: resource.bucket) else {
fatalError("Incorrect URL generated for the given resource: \(resource)")
}
let deferred = Deferred<Result<Data, Error>>(value: nil, defaultQueue: .main)
self.session.dataRequest(with: resource.bucket.url.appendingPathComponent(filename)) { data, response, error in
self.session.dataRequest(with: url.appendingPathComponent(filename)) { data, response, error in
if let error = error {
deferred.fill(.failure(error))
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ class NewTabPageViewController: UIViewController, Themeable {
collectionView.backgroundView = backgroundButtonsView

feedOverlayView.headerView.settingsButton.addTarget(self, action: #selector(tappedBraveTodaySettings), for: .touchUpInside)
if !AppConstants.buildChannel.isPublic {
feedOverlayView.headerView.settingsButton.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(longPressedBraveTodaySettingsButton)))
}
feedOverlayView.newContentAvailableButton.addTarget(self, action: #selector(tappedNewContentAvailable), for: .touchUpInside)

backgroundButtonsView.tappedActiveButton = { [weak self] sender in
Expand Down Expand Up @@ -697,6 +700,14 @@ class NewTabPageViewController: UIViewController, Themeable {
UIImpactFeedbackGenerator(style: .medium).bzzt()
present(alert, animated: true, completion: nil)
}

@objc private func longPressedBraveTodaySettingsButton() {
assert(!AppConstants.buildChannel.isPublic,
"Debug settings are not accessible on public builds")
let settings = BraveTodayDebugSettingsController(dataSource: feedDataSource)
let container = UINavigationController(rootViewController: settings)
present(container, animated: true)
}
}

extension NewTabPageViewController: PreferencesObserver {
Expand Down
145 changes: 145 additions & 0 deletions Client/Frontend/Settings/BraveTodayDebugSettingsController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2020 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

import UIKit
import Static

extension FeedDataSource.Environment {
fileprivate var name: String {
switch self {
case .dev: return "Dev"
case .staging: return "Staging"
case .production: return "Production"
}
}
}

extension FeedDataSource {
fileprivate func description(of state: State) -> String {
switch state {
case .initial:
return ""
case .loading:
return "Loading"
case .success:
return "Success"
case .failure(let error):
return "Error: \(error.localizedDescription)"
}
}
fileprivate func detailRows(for state: State) -> [Row] {
switch state {
case .initial:
return []
case .loading(let previousState):
return [Row(text: "Previous State", detailText: description(of: previousState))]
case .success(let cards):
return [
Row(text: "Sources", detailText: "\(sources.count)"),
Row(text: "Cards Generated", detailText: "\(cards.count)")
]
case .failure(let error):
return [
Row(text: "Error Details", detailText: error.localizedDescription, cellClass: MultilineSubtitleCell.self)
]
}
}
}

class BraveTodayDebugSettingsController: TableViewController {
private let feedDataSource: FeedDataSource

init(dataSource: FeedDataSource) {
feedDataSource = dataSource
if #available(iOS 13.0, *) {
super.init(style: .insetGrouped)
} else {
super.init(style: .grouped)
}
}

@available(*, unavailable)
required init(coder: NSCoder) {
fatalError()
}

override func viewDidLoad() {
super.viewDidLoad()

title = "Brave Today QA Settings"

reloadData()

if navigationController?.viewControllers.first === self {
navigationItem.rightBarButtonItem = .init(barButtonSystemItem: .done, target: self, action: #selector(tappedDone))
}
}

func reloadData() {
dataSource.sections = [
.init(
rows: [
Row(text: "Environment", detailText: feedDataSource.environment.name, selection: { [unowned self] in
let picker = TodayEnvironmentPicker(selectedEnvironment: feedDataSource.environment) { [unowned self] newEnvironment in
feedDataSource.environment = newEnvironment
self.reloadData()
self.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(picker, animated: true)
}, accessory: .disclosureIndicator)
],
footer: .title("Changing the environment will purge all cached resources immediately.")
),
.init(
rows: [
Row(text: "State", detailText: feedDataSource.description(of: feedDataSource.state)),
] + feedDataSource.detailRows(for: feedDataSource.state)
)
]
}

@objc private func tappedDone() {
dismiss(animated: true)
}
}

private class TodayEnvironmentPicker: TableViewController {
let selectedEnvironment: FeedDataSource.Environment
let environmentUpdated: (FeedDataSource.Environment) -> Void
init(selectedEnvironment: FeedDataSource.Environment,
environmentUpdated: @escaping (FeedDataSource.Environment) -> Void) {
self.selectedEnvironment = selectedEnvironment
self.environmentUpdated = environmentUpdated
if #available(iOS 13.0, *) {
super.init(style: .insetGrouped)
} else {
super.init(style: .grouped)
}
}

@available(*, unavailable)
required init(coder: NSCoder) {
fatalError()
}

override func viewDidLoad() {
super.viewDidLoad()

title = "Environment"
navigationItem.leftBarButtonItem = .init(barButtonSystemItem: .cancel, target: self, action: #selector(cancelPicker))

dataSource.sections = [
.init(rows: FeedDataSource.Environment.allCases.map { environment in
Row(text: environment.name, selection: { [unowned self] in
self.environmentUpdated(environment)
}, accessory: environment == selectedEnvironment ? .checkmark : .none)
})
]
}

@objc private func cancelPicker() {
navigationController?.popViewController(animated: true)
}
}
8 changes: 8 additions & 0 deletions Client/Frontend/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ class SettingsViewController: TableViewController {
navigationController?.pushViewController(settings, animated: true)
}

private func displayBraveTodayDebugMenu() {
let settings = BraveTodayDebugSettingsController(dataSource: feedDataSource)
navigationController?.pushViewController(settings, animated: true)
}

private var theme: Theme {
Theme.of(tabManager.selectedTab)
}
Expand Down Expand Up @@ -480,6 +485,9 @@ class SettingsViewController: TableViewController {
Row(text: "View Rewards Debug Menu", selection: { [unowned self] in
self.displayRewardsDebugMenu()
}, accessory: .disclosureIndicator, cellClass: MultilineValue1Cell.self),
Row(text: "View Brave Today Debug Menu", selection: { [unowned self] in
self.displayBraveTodayDebugMenu()
}, accessory: .disclosureIndicator, cellClass: MultilineValue1Cell.self),
Row(text: "Load all QA Links", selection: { [unowned self] in
let url = URL(string: "https://raw.githubusercontent.com/brave/qa-resources/master/testlinks.json")!
let string = try? String(contentsOf: url)
Expand Down

0 comments on commit f596798

Please sign in to comment.