Files
lockdown-iOS-mirror/LockdowniOS/ImportBlockListViewController.swift
2023-06-23 17:11:36 +03:00

310 lines
11 KiB
Swift

//
// ImportBlockListViewController.swift
// LockdownSandbox
//
// Created by Aliaksandr Dvoineu on 3.04.23.
//
import UIKit
import UniformTypeIdentifiers
import MobileCoreServices
final class ImportBlockListViewController: UIViewController, UIDocumentPickerDelegate, DomainListSaveable {
// MARK: - Properties
var importCompletion: (() -> ())?
var titleName = "Import Block List"
private lazy var navigationView: ConfiguredNavigationView = {
let view = ConfiguredNavigationView()
view.rightNavButton.setTitle(NSLocalizedString("CANCEL", comment: ""), for: .normal)
view.titleLabel.text = titleName
view.rightNavButton.tintColor = .tunnelsBlue
view.rightNavButton.addTarget(self, action: #selector(cancel), for: .touchUpInside)
return view
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("Import Domains from file", comment: "")
label.textColor = .label
label.font = fontBold17
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var descriptionParagraph1: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("Take control of your browsing experience! Import your own custom block list and say goodbye to pesky trackers for good.", comment: "")
label.textColor = .label
label.font = fontRegular14
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var descriptionParagraph2: UILabel = {
let label = UILabel()
label.textColor = .label
label.font = fontRegular14
let highlightedText = NSLocalizedString("comma-separated values (.csv)*", comment: "a bold part of description")
label.text = NSLocalizedString("Simply select the ", comment: "") +
highlightedText +
NSLocalizedString(" file with the domains you want to block and import it. It's that easy!", comment: "")
label.highlight(highlightedText, font: UIFont.boldLockdownFont(size: 14))
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var selectFromFilesButton: UIButton = {
let button = UIButton(type: .system)
button.tintColor = .white
button.backgroundColor = .tunnelsBlue
button.layer.cornerRadius = 28
button.setTitle(NSLocalizedString("Select from Files", comment: ""), for: .normal)
button.setImage(UIImage(named: "icn_csv_file"), for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
button.titleLabel?.font = fontBold17
button.addTarget(self, action: #selector(selectFromFiles), for: .touchUpInside)
return button
}()
private lazy var descriptionParagraph3: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("* The .csv file should contain a single column of domains and/or sub-domains. NO headers, NO additional columns, and NO URLs.", comment: "")
label.textColor = .label
label.font = fontRegular14
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var vStackView: UIStackView = {
let stackView = UIStackView()
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(descriptionParagraph1)
stackView.addArrangedSubview(descriptionParagraph2)
stackView.addArrangedSubview(selectFromFilesButton)
stackView.addArrangedSubview(descriptionParagraph3)
stackView.axis = .vertical
stackView.alignment = .leading
stackView.distribution = .equalSpacing
stackView.spacing = 24
return stackView
}()
private lazy var pasteFromClipboardTitle: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("Paste multiple domains from clipboard", comment: "")
label.textColor = .label
label.font = fontBold15
label.textColor = .label
label.numberOfLines = 1
label.textAlignment = .left
return label
}()
private lazy var separator: UIView = {
let view = UIView()
view.backgroundColor = .gray
return view
}()
private lazy var pasteFromClipboardText: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("You can copy existing data from a spreadsheet (like and Excel workbook or Google Sheet) and paste it in the field below.", comment: "")
label.textColor = .label
label.font = fontRegular14
label.textColor = .label
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var pasteFromClipboardTextfield: UITextField = {
let textFieled = UITextField()
textFieled.textColor = .label
textFieled.font = fontRegular14
textFieled.textColor = .label
textFieled.textAlignment = .left
textFieled.contentVerticalAlignment = .top
textFieled.borderStyle = .line
return textFieled
}()
private lazy var blockPastedDomainsButton: UIButton = {
let button = UIButton(type: .system)
button.tintColor = .white
button.backgroundColor = .gray
button.layer.cornerRadius = 28
button.setTitle(NSLocalizedString("Block Pasted Domains", comment: ""), for: .normal)
button.titleLabel?.font = fontBold15
button.addTarget(self, action: #selector(blockPastedDomains), for: .touchUpInside)
return button
}()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .secondarySystemBackground
configureUI()
}
// MARK: - Configure UI
func configureUI() {
view.addSubview(navigationView)
navigationView.anchors.leading.pin()
navigationView.anchors.trailing.pin()
navigationView.anchors.top.safeAreaPin()
view.addSubview(vStackView)
vStackView.anchors.top.spacing(48, to: navigationView.anchors.bottom)
vStackView.anchors.leading.marginsPin()
vStackView.anchors.trailing.marginsPin()
selectFromFilesButton.anchors.leading.marginsPin()
selectFromFilesButton.anchors.trailing.marginsPin()
selectFromFilesButton.anchors.height.equal(56)
}
}
// MARK: - Private functions
extension ImportBlockListViewController {
@objc func cancel() {
let viewController = BlockListViewController()
viewController.reloadCustomBlockedLists()
dismiss(animated: true)
}
private func saveImportedDomains(
_ content: Set<String>,
toNewListName newListName: String
) {
if !content.isEmpty {
addBlockedList(listName: newListName)
var allData = getBlockedLists()
let importedList = UserBlockListsGroup(name: newListName, domains: content)
allData.userBlockListsDefaults[importedList.name] = importedList
let encodedData = try? JSONEncoder().encode(allData)
defaults.set(encodedData, forKey: kUserBlockedLists)
importCompletion?()
}
closeScreen(withSuccess: !content.isEmpty)
}
private func closeScreen(withSuccess success: Bool) {
dismiss(animated: true) {
let title = success
? NSLocalizedString("Success!", comment: "")
: NSLocalizedString("Error", comment: "")
let message = success
? NSLocalizedString("The list has been imported successfully. You can start blocking the list's domains", comment: "")
: NSLocalizedString("Your list of domains is empty or in the wrong format", comment: "")
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert
)
alert.addAction(
UIAlertAction(
title: NSLocalizedString("Close", comment: ""),
style: .default,
handler: nil
)
)
UIApplication.getTopMostViewController()?.present(alert, animated: true, completion: nil)
}
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else {
return
}
do {
let content = csvProcessing(data: try String(contentsOf: url, encoding: .utf8))
guard !content.isEmpty else {
closeScreen(withSuccess: false)
return
}
showCreateList(
initialListName: nil,
forDomainList: content
) { [weak self] in
self?.saveImportedDomains($0, toNewListName: $1)
}
} catch {
dismiss(animated: true) {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""),
message: NSLocalizedString("Unable to import the list. Please try again or contact support for assistance", comment: ""),
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Close", comment: ""),
style: .default,
handler: nil))
UIApplication.getTopMostViewController()?.present(alert, animated: true, completion: nil)
}
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
print("view was cancelled")
dismiss(animated: true, completion: nil)
}
func csvProcessing(data: String) -> Set<String> {
var domains = Set<String>()
var arrayOfDomains = [String]()
if data.contains("\r\n") {
arrayOfDomains = data.components(separatedBy: "\r\n")
} else if data.contains("\r") {
arrayOfDomains = data.components(separatedBy: "\r")
} else if data.contains("\n") {
arrayOfDomains = data.components(separatedBy: "\n")
} else if data.contains(",") {
arrayOfDomains = data.components(separatedBy: ",")
}
for domain in arrayOfDomains {
if domain.isValid(.domainName) {
domains.insert(domain)
}
}
return domains
}
@objc func selectFromFiles() {
if #available(iOS 14.0, *) {
let supportedFiles: [UTType] = [UTType.data]
let controller = UIDocumentPickerViewController(forOpeningContentTypes: supportedFiles, asCopy: true)
controller.delegate = self
controller.modalPresentationStyle = .formSheet
controller.allowsMultipleSelection = false
present(controller, animated: true)
}
}
@objc func blockPastedDomains() {
// TODO: future implementation according to the requirements
}
}