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

Commit

Permalink
Add SubmitReportView
Browse files Browse the repository at this point in the history
  • Loading branch information
cuba committed Sep 22, 2023
1 parent edefc04 commit 5e94762
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 136 deletions.
86 changes: 0 additions & 86 deletions Sources/Brave/Frontend/Shields/ReportBrokenSiteView.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/Brave/Frontend/Shields/ShieldsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Foundation
import Shared
import BraveShared
import UIKit
import SwiftUI

extension ShieldsViewController {
class View: UIView {
Expand Down Expand Up @@ -38,7 +39,6 @@ extension ShieldsViewController {
$0.isHidden = true
}

let reportBrokenSiteView = ReportBrokenSiteView()
let siteReportedView = SiteReportedView()

override init(frame: CGRect) {
Expand Down
84 changes: 58 additions & 26 deletions Sources/Brave/Frontend/Shields/ShieldsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import UIKit
import Growth
import BraveCore
import BraveVPN
import SwiftUI

/// Displays shield settings and shield stats for a given URL
class ShieldsViewController: UIViewController, PopoverContentComponent {
Expand Down Expand Up @@ -191,6 +192,10 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
shieldsView.contentView = view
}
}

private var preferredWidth: CGFloat {
return min(360, UIScreen.main.bounds.width - 20)
}

private func updatePreferredContentSize() {
guard let visibleView = shieldsView.contentView else { return }
Expand All @@ -208,6 +213,30 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
height: height
)
}

private lazy var reportViewController: UIHostingController<SubmitReportView>? = {
guard let url = url else { return nil }
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
components?.fragment = nil
components?.queryItems = nil
guard let cleanedURL = components?.url else { return nil }

let viewController = UIHostingController(rootView: SubmitReportView(
width: preferredWidth, url: cleanedURL,
dismiss: tappedCancelReportingButton,
submit: { additionalDetails, contactInfo in
self.tappedSubmitReportingButton(url: cleanedURL, additionalDetails: additionalDetails, contactInfo: contactInfo)
}
))

guard let swiftUIView = viewController.view else { return nil }
swiftUIView.alpha = 0
swiftUIView.translatesAutoresizingMaskIntoConstraints = false
addChild(viewController)
view.addSubview(swiftUIView)
viewController.didMove(toParent: self)
return viewController
}()

// MARK: -

Expand Down Expand Up @@ -239,7 +268,6 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
let normalizedDisplayHost = URLFormatter.formatURLOrigin(forDisplayOmitSchemePathAndTrivialSubdomains: url?.absoluteString ?? "")

shieldsView.simpleShieldView.hostLabel.text = normalizedDisplayHost
shieldsView.reportBrokenSiteView.urlLabel.text = url?.domainURL.absoluteString
shieldsView.simpleShieldView.shieldsSwitch.addTarget(self, action: #selector(shieldsOverrideSwitchValueChanged), for: .valueChanged)
shieldsView.advancedShieldView.siteTitle.titleLabel.text = normalizedDisplayHost.uppercased()
shieldsView.advancedShieldView.globalControlsButton.addTarget(self, action: #selector(tappedGlobalShieldsButton), for: .touchUpInside)
Expand All @@ -250,8 +278,6 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
shieldsView.simpleShieldView.blockCountView.shareButton.addTarget(self, action: #selector(tappedShareShieldsButton), for: .touchUpInside)

shieldsView.simpleShieldView.reportSiteButton.addTarget(self, action: #selector(tappedReportSiteButton), for: .touchUpInside)
shieldsView.reportBrokenSiteView.cancelButton.addTarget(self, action: #selector(tappedCancelReportingButton), for: .touchUpInside)
shieldsView.reportBrokenSiteView.submitButton.addTarget(self, action: #selector(tappedSubmitReportingButton), for: .touchUpInside)

updateShieldBlockStats()

Expand Down Expand Up @@ -317,36 +343,42 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
}

@objc private func tappedReportSiteButton() {
updateContentView(to: shieldsView.reportBrokenSiteView, animated: true)
guard let view = reportViewController?.view else {
assertionFailure()
return
}

updateContentView(to: view, animated: true)
}

@objc private func tappedCancelReportingButton() {
updateContentView(to: shieldsView.stackView, animated: true)
}

@objc private func tappedSubmitReportingButton() {
if let url = url {
Task { @MainActor in
let domain = Domain.getOrCreate(forUrl: url, persistent: !tab.isPrivate)
let report = WebcompatReporter.Report(
fullUrl: url,
areShieldsEnabled: !domain.areAllShieldsOff,
adBlockLevel: domain.blockAdsAndTrackingLevel,
fingerprintProtectionLevel: domain.finterprintProtectionLevel,
adBlockListTitles: FilterListStorage.shared.filterLists.compactMap({ filterList -> String? in
guard filterList.isEnabled else { return nil }
return filterList.entry.title
}),
isVPNEnabled: BraveVPN.isConnected
)
await WebcompatReporter.send(report: report)
try await Task.sleep(nanoseconds: NSEC_PER_SEC * 2)
guard !self.isBeingDismissed else { return }
self.dismiss(animated: true)
}
@objc private func tappedSubmitReportingButton(url: URL, additionalDetails: String, contactInfo: String) {
Task { @MainActor in
let domain = Domain.getOrCreate(forUrl: url, persistent: !tab.isPrivate)

let report = WebcompatReporter.Report(
cleanedURL: url,
additionalDetails: additionalDetails,
contactInfo: contactInfo,
areShieldsEnabled: !domain.areAllShieldsOff,
adBlockLevel: domain.blockAdsAndTrackingLevel,
fingerprintProtectionLevel: domain.finterprintProtectionLevel,
adBlockListTitles: FilterListStorage.shared.filterLists.compactMap({ filterList -> String? in
guard filterList.isEnabled else { return nil }
return filterList.entry.title
}),
isVPNEnabled: BraveVPN.isConnected
)

await WebcompatReporter.send(report: report)
try await Task.sleep(nanoseconds: NSEC_PER_SEC * 2)
guard !self.isBeingDismissed else { return }
self.dismiss(animated: true)
}

updateContentView(to: shieldsView.siteReportedView, animated: true)
}

Expand Down
88 changes: 88 additions & 0 deletions Sources/Brave/Frontend/Shields/SubmitReportView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// SwiftUIView.swift
//
//
// Created by Jacob on 2023-09-21.
//

import SwiftUI
import Strings
import BraveUI
import DesignSystem
import BraveShields

struct SubmitReportView: View {
let width: CGFloat
let url: URL
let dismiss: () -> Void
let submit: (String, String) -> Void

@State var additionalDetails = ""
@State var contactDetails = ""

private var borderShape: some InsettableShape {
RoundedRectangle(cornerRadius: 4, style: .continuous)
}

private var textEditor: some View {
TextEditor(text: $additionalDetails)
.modifier(BaseBraveTextEditorStyleModifier(
placeholder: Strings.Shields.reportBrokenAdditionalDetails,
isPlaceholderVisible: additionalDetails.isEmpty
))
}

var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text(Strings.Shields.reportABrokenSite)
.font(.title)
Text(Strings.Shields.reportBrokenSiteBody1)
Text(url.absoluteString)
.foregroundStyle(Color(braveSystemName: .textInteractive))
Text(Strings.Shields.reportBrokenSiteBody2)
.font(.footnote)

textEditor

VStack(alignment: .leading, spacing: 4) {
Text(Strings.Shields.reportBrokenContactMe).font(.caption)
TextField(
Strings.Shields.reportBrokenContactMe,
text: $contactDetails, prompt: Text(Strings.Shields.reportBrokenContactMeSuggestions)
)
.textFieldStyle(BraveTextFieldStyle())
.autocorrectionDisabled()
.textInputAutocapitalization(.never)
}

HStack {
Spacer()
Button(Strings.cancelButtonTitle, action: dismiss)
.buttonStyle(BraveOutlineButtonStyle(size: .large))
.multilineTextAlignment(.center)

Button(Strings.Shields.reportBrokenSubmitButtonTitle, action: {
submit(additionalDetails, contactDetails)
})
.buttonStyle(BraveFilledButtonStyle(size: .large))
.multilineTextAlignment(.center)
}
}
.padding()
.foregroundStyle(Color(braveSystemName: .textSecondary))
}
.foregroundStyle(Color(braveSystemName: .textSecondary))
.frame(maxWidth: width)
}
}

#Preview {
SubmitReportView(
width: 300,
url: URL(string: "https://brave.com/privacy-features")!) {
// Do nothing
} submit: { _, _ in
// Do nothing
}
}
25 changes: 6 additions & 19 deletions Sources/BraveShields/WebcompatReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public class WebcompatReporter {
/// The raw values of the web-report.
public struct Report {
/// The URL of the broken site.
/// - Note: This is the full url and will be used to extract all relevant information
let fullUrl: URL
/// - Note: This needs to be the cleaned up version with query params and fragments removed (as seen in the UI)
let cleanedURL: URL
/// Any user input details
let additionalDetails: String?
/// Any user input contact details that may be provided
Expand All @@ -31,22 +31,15 @@ public class WebcompatReporter {
let isVPNEnabled: Bool

var domain: String? {
return fullUrl.normalizedHost() != nil ? fullUrl.domainURL.absoluteString : fullUrl.baseDomain
}

var cleanedURL: URL? {
var components = URLComponents(url: fullUrl, resolvingAgainstBaseURL: false)
components?.fragment = nil
components?.queryItems = nil
return components?.url
return cleanedURL.normalizedHost() != nil ? cleanedURL.domainURL.absoluteString : cleanedURL.baseDomain
}

public init(
fullUrl: URL, additionalDetails: String? = nil, contactInfo: String? = nil,
cleanedURL: URL, additionalDetails: String? = nil, contactInfo: String? = nil,
areShieldsEnabled: Bool, adBlockLevel: ShieldLevel, fingerprintProtectionLevel: ShieldLevel,
adBlockListTitles: [String], isVPNEnabled: Bool
) {
self.fullUrl = fullUrl
self.cleanedURL = cleanedURL
self.additionalDetails = additionalDetails
self.contactInfo = contactInfo
self.areShieldsEnabled = areShieldsEnabled
Expand Down Expand Up @@ -94,15 +87,9 @@ public class WebcompatReporter {
))
}

guard let cleanedURL = report.cleanedURL else {
throw EncodingError.invalidValue(CodingKeys.domain, EncodingError.Context(
codingPath: encoder.codingPath, debugDescription: "Cannot strip fragments or query params"
))
}

var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(cleanedURL.absoluteString, forKey: .url)
try container.encode(domain, forKey: .domain)
try container.encode(report.cleanedURL.absoluteString, forKey: .url)
try container.encodeIfPresent(report.additionalDetails, forKey: .additionalDetails)
try container.encodeIfPresent(report.contactInfo, forKey: .contactInfo)
try container.encodeIfPresent(languageCode, forKey: .languages)
Expand Down
Loading

0 comments on commit 5e94762

Please sign in to comment.