Skip to content

Commit c9cc035

Browse files
committedApr 14, 2023
[KB-2267]: changed data structure, export feature changes, code refactoring
1 parent e4cd2f4 commit c9cc035

15 files changed

+337
-100
lines changed
 

‎FirewallUtilities.swift

+62-25
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,20 @@ struct LockdownDefaults : Codable {
4545

4646
struct UserBlockListsGroup: Codable {
4747
var name: String
48-
var enabled : Bool
49-
var domains : Dictionary<String, Bool>
48+
var enabled : Bool = true
49+
var domains : [Domains] = []
5050
var description: String?
5151
}
5252

5353
struct UserBlockListsDefaults: Codable {
5454
var userBlockListsDefaults: Dictionary<String, UserBlockListsGroup>
5555
}
5656

57+
struct Domains: Codable {
58+
var name: String
59+
var isBlocked: Bool = true
60+
}
61+
5762
// MARK: - Block Metrics & Block Log
5863

5964
let currentCalendar = Calendar.current
@@ -438,38 +443,70 @@ func deleteUserBlockedDomain(domain: String) {
438443

439444
// MARK: - User blocked lists
440445

441-
func getUserBlockedList() -> Dictionary<String, Any> {
442-
if let lists = defaults.dictionary(forKey: kUserBlockedLists) {
443-
return lists
446+
func getBlockedLists() -> UserBlockListsDefaults {
447+
guard let listsDefaultsData = defaults.object(forKey: kUserBlockedLists) as? Data else {
448+
return UserBlockListsDefaults(userBlockListsDefaults: [:])
444449
}
445-
return Dictionary()
450+
451+
guard let listDefaults = try? JSONDecoder().decode(UserBlockListsDefaults.self, from: listsDefaultsData) else {
452+
return UserBlockListsDefaults(userBlockListsDefaults: [:])
453+
}
454+
return listDefaults
446455
}
447456

448-
func addUserBlockedList(list: String) {
449-
var lists = getUserBlockedList()
450-
lists[list] = NSNumber(value: true)
451-
defaults.set(lists, forKey: kUserBlockedLists)
457+
func addBlockedList(listName: String) {
458+
var data = getBlockedLists()
459+
data.userBlockListsDefaults[listName] = UserBlockListsGroup(name: listName, enabled: false)
460+
let encodedData = try? JSONEncoder().encode(data)
461+
defaults.set(encodedData, forKey: kUserBlockedLists)
452462
}
453463

454-
func setUserBlockedList(list: String, enabled: Bool) {
455-
var lists = getUserBlockedList()
456-
lists[list] = NSNumber(value: enabled)
457-
defaults.set(lists, forKey: kUserBlockedLists)
464+
func deleteBlockedList(listName: String) {
465+
var data = getBlockedLists()
466+
data.userBlockListsDefaults[listName] = nil
467+
let encodedData = try? JSONEncoder().encode(data)
468+
defaults.set(encodedData, forKey: kUserBlockedLists)
458469
}
459470

460-
func deleteUserBlockedList(list: String) {
461-
var lists = getUserBlockedList()
462-
lists[list] = nil
463-
defaults.set(lists, forKey: kUserBlockedLists)
471+
func addDomainToBlockedList(domain: String, for listName: String) {
472+
var data = getBlockedLists()
473+
data.userBlockListsDefaults[listName]?.domains.append(Domains(name: domain))
474+
let encodedData = try? JSONEncoder().encode(data)
475+
defaults.set(encodedData, forKey: kUserBlockedLists)
464476
}
465477

466-
func getUserBlockedLists() -> UserBlockListsDefaults {
467-
guard let blockListsdefaultsData = defaults.object(forKey: kUserBlockedLists) as? Data else {
468-
return UserBlockListsDefaults(userBlockListsDefaults: [:])
478+
extension Domains {
479+
func exportToURL() -> URL? {
480+
guard let encoded = try? JSONEncoder().encode(self) else { return nil }
481+
482+
let documents = FileManager.default.urls(
483+
for: .documentDirectory,
484+
in: .userDomainMask
485+
).first
486+
487+
guard let path = documents?.appendingPathComponent("/\(name).csv") else {
488+
return nil
489+
}
490+
491+
do {
492+
try encoded.write(to: path, options: .atomicWrite)
493+
return path
494+
} catch {
495+
print(error.localizedDescription)
496+
return nil
497+
}
469498
}
470-
guard let blockListsdefaults = try? PropertyListDecoder().decode(UserBlockListsDefaults.self, from: blockListsdefaultsData) else {
471-
return UserBlockListsDefaults(userBlockListsDefaults: [:])
499+
500+
static func importData(from url: URL) {
501+
guard
502+
let data = try? Data(contentsOf: url),
503+
let domain = try? JSONDecoder().decode(Domains.self, from: data)
504+
505+
506+
else { return }
507+
addDomainToBlockedList(domain: domain.name, for: "oop")
508+
509+
510+
try? FileManager.default.removeItem(at: url)
472511
}
473-
return blockListsdefaults
474512
}
475-

‎LockdowniOS.xcodeproj/project.pbxproj

+8-4
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
402D24B829D59B4400A5AB60 /* EmptyListsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24B729D59B4400A5AB60 /* EmptyListsView.swift */; };
206206
402D24CB29D87B5A00A5AB60 /* ListsSubmenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24CA29D87B5A00A5AB60 /* ListsSubmenuView.swift */; };
207207
402D24D429D87F4500A5AB60 /* CustomBlockedTableHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D24D329D87F4500A5AB60 /* CustomBlockedTableHeader.swift */; };
208-
402D251629E514CF00A5AB60 /* MoveToLsisViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251529E514CF00A5AB60 /* MoveToLsisViewController.swift */; };
208+
402D251629E514CF00A5AB60 /* MoveToListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251529E514CF00A5AB60 /* MoveToListViewController.swift */; };
209209
402D251929E517E100A5AB60 /* ConfiguredNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251829E517E100A5AB60 /* ConfiguredNavigationView.swift */; };
210210
402D251B29E519B500A5AB60 /* CustomTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251A29E519B500A5AB60 /* CustomTableView.swift */; };
211211
402D251F29E52D6A00A5AB60 /* EditDomainsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D251E29E52D6A00A5AB60 /* EditDomainsViewController.swift */; };
@@ -218,6 +218,7 @@
218218
402D252F29E6357700A5AB60 /* ListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D252E29E6357700A5AB60 /* ListDetailViewController.swift */; };
219219
402D253129E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253029E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift */; };
220220
402D253329E6588000A5AB60 /* ListDescriptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253229E6588000A5AB60 /* ListDescriptionViewController.swift */; };
221+
402D253B29E8F9A400A5AB60 /* JSONSerialization+Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D253A29E8F9A400A5AB60 /* JSONSerialization+Extentions.swift */; };
221222
4A86219093026DE70A097E79 /* Pods-LockdownTests-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8DA68459884385F76BF86234 /* Pods-LockdownTests-metadata.plist */; };
222223
54F0B1A0273200B0002F3630 /* FirewallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCA4F4022F252720017740D /* FirewallController.swift */; };
223224
5647ACFEBBAB001FAE27CAF9 /* Pods-LockdownTunnel-settings-metadata.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6F089C7008AB8F59DE3EA7BD /* Pods-LockdownTunnel-settings-metadata.plist */; };
@@ -618,7 +619,7 @@
618619
402D24B729D59B4400A5AB60 /* EmptyListsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyListsView.swift; sourceTree = "<group>"; };
619620
402D24CA29D87B5A00A5AB60 /* ListsSubmenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListsSubmenuView.swift; sourceTree = "<group>"; };
620621
402D24D329D87F4500A5AB60 /* CustomBlockedTableHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomBlockedTableHeader.swift; sourceTree = "<group>"; };
621-
402D251529E514CF00A5AB60 /* MoveToLsisViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoveToLsisViewController.swift; sourceTree = "<group>"; };
622+
402D251529E514CF00A5AB60 /* MoveToListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoveToListViewController.swift; sourceTree = "<group>"; };
622623
402D251829E517E100A5AB60 /* ConfiguredNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfiguredNavigationView.swift; sourceTree = "<group>"; };
623624
402D251A29E519B500A5AB60 /* CustomTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableView.swift; sourceTree = "<group>"; };
624625
402D251E29E52D6A00A5AB60 /* EditDomainsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditDomainsViewController.swift; sourceTree = "<group>"; };
@@ -631,6 +632,7 @@
631632
402D252E29E6357700A5AB60 /* ListDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDetailViewController.swift; sourceTree = "<group>"; };
632633
402D253029E635CB00A5AB60 /* DomainsBlockedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsBlockedTableViewCell.swift; sourceTree = "<group>"; };
633634
402D253229E6588000A5AB60 /* ListDescriptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDescriptionViewController.swift; sourceTree = "<group>"; };
635+
402D253A29E8F9A400A5AB60 /* JSONSerialization+Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONSerialization+Extentions.swift"; sourceTree = "<group>"; };
634636
4CA426CE326B009F3E4493D1 /* Pods_Lockdown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lockdown.framework; sourceTree = BUILT_PRODUCTS_DIR; };
635637
50F9BE503587CE4933CB7983 /* Pods-Lockdown-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown-settings-metadata.plist"; path = "Settings.bundle/Pods-Lockdown-settings-metadata.plist"; sourceTree = "<group>"; };
636638
50FB8ADA1D444FD9486F2D44 /* Pods-Lockdown Firewall Widget-settings-metadata.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "Pods-Lockdown Firewall Widget-settings-metadata.plist"; path = "LockdowniOS/Settings.bundle/Pods-Lockdown Firewall Widget-settings-metadata.plist"; sourceTree = "<group>"; };
@@ -1154,7 +1156,7 @@
11541156
isa = PBXGroup;
11551157
children = (
11561158
402D251E29E52D6A00A5AB60 /* EditDomainsViewController.swift */,
1157-
402D251529E514CF00A5AB60 /* MoveToLsisViewController.swift */,
1159+
402D251529E514CF00A5AB60 /* MoveToListViewController.swift */,
11581160
);
11591161
name = Controllers;
11601162
sourceTree = "<group>";
@@ -1217,6 +1219,7 @@
12171219
children = (
12181220
7C1AE07F248028F40000A7D3 /* UIKit+Extensions.swift */,
12191221
7C422EA425279724007F9C22 /* Align.swift */,
1222+
402D253A29E8F9A400A5AB60 /* JSONSerialization+Extentions.swift */,
12201223
);
12211224
name = Extensions;
12221225
sourceTree = "<group>";
@@ -2362,7 +2365,7 @@
23622365
3D47CDAF22F3C3F3003BD7F7 /* NVActivityIndicatorAnimationDelegate.swift in Sources */,
23632366
7C1AE080248028F40000A7D3 /* UIKit+Extensions.swift in Sources */,
23642367
3DBD57AC22FBDFE300DE189F /* WhitelistCell.swift in Sources */,
2365-
402D251629E514CF00A5AB60 /* MoveToLsisViewController.swift in Sources */,
2368+
402D251629E514CF00A5AB60 /* MoveToListViewController.swift in Sources */,
23662369
3DF2455423A2F8A400E46613 /* EmailSignUpViewController.swift in Sources */,
23672370
3D47CDB522F3C3F3003BD7F7 /* NVActivityIndicatorAnimationBallRotate.swift in Sources */,
23682371
3D47CDC922F3C3F3003BD7F7 /* NVActivityIndicatorAnimationAudioEqualizer.swift in Sources */,
@@ -2376,6 +2379,7 @@
23762379
3D5F5A0C23109ABB004C3860 /* WhatIsVpnViewController.swift in Sources */,
23772380
A174CCAE22B15B1000F1B840 /* BlockListViewController.swift in Sources */,
23782381
3DAF73522768572300D97BB0 /* FirewallUtilities.swift in Sources */,
2382+
402D253B29E8F9A400A5AB60 /* JSONSerialization+Extentions.swift in Sources */,
23792383
3D47CDD022F3C3F3003BD7F7 /* NVActivityIndicatorAnimationBallPulseSync.swift in Sources */,
23802384
402D252729E5843300A5AB60 /* ImportBlockListViewController.swift in Sources */,
23812385
);

‎LockdowniOS/AppDelegate.swift

+3
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
359359
}
360360

361361
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
362+
363+
guard url.pathExtension == "csv" else { return false }
364+
Domains.importData(from: url)
362365

363366
guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
364367
let host = components.host else {

‎LockdowniOS/BlockListViewController.swift

+19-11
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,21 @@ final class BlockListViewController: BaseViewController {
137137

138138
view.backgroundColor = .secondarySystemBackground
139139

140+
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissView))
141+
tap.cancelsTouchesInView = false
142+
view.addGestureRecognizer(tap)
143+
140144
configure()
141145
configureCuratedBlockedDomainsTableView()
142146
configureCustomBlockedListsTableView()
143147
configureCustomBlockedDomainsTableView()
144148
}
145149

150+
override func viewWillAppear(_ animated: Bool) {
151+
super.viewWillAppear(animated)
152+
reloadCustomBlockedLists()
153+
}
154+
146155
private func configure() {
147156
view.addSubview(customNavigationView)
148157
customNavigationView.anchors.leading.pin()
@@ -192,8 +201,6 @@ final class BlockListViewController: BaseViewController {
192201
listsSubmenuView.anchors.top.spacing(60, to: paragraphLabel.anchors.bottom)
193202

194203
customBlockedListsTableView.deselectsCellsAutomatically = true
195-
196-
reloadCustomBlockedLists()
197204
}
198205

199206
private func configureCustomBlockedDomainsTableView() {
@@ -236,7 +243,7 @@ final class BlockListViewController: BaseViewController {
236243
func reloadCustomBlockedLists() {
237244
customBlockedListsTableView.clear()
238245
customBlockedLists = {
239-
let lists = getUserBlockedLists().userBlockListsDefaults
246+
let lists = getBlockedLists().userBlockListsDefaults
240247
let sorted = lists.sorted(by: { $0.key < $1.key })
241248
return Array(sorted.map(\.value))
242249
}()
@@ -317,7 +324,7 @@ final class BlockListViewController: BaseViewController {
317324
func saveNewList(userEnteredListName: String) {
318325
didMakeChange = true
319326
DDLogInfo("Adding custom list - \(userEnteredListName)")
320-
addUserBlockedList(list: userEnteredListName)
327+
addBlockedList(listName: userEnteredListName)
321328
reloadCustomBlockedLists()
322329
}
323330

@@ -368,7 +375,7 @@ extension BlockListViewController {
368375
}.onSelect { [unowned self] in
369376
self.didMakeChange = true
370377
let vc = ListSettingsViewController()
371-
vc.titleName = list.name
378+
vc.listName = list.name
372379
vc.blockedList = list
373380
vc.blockListVC = self
374381
navigationController?.pushViewController(vc, animated: true)
@@ -438,9 +445,9 @@ extension BlockListViewController {
438445
if let txtField = alertController.textFields?.first, let text = txtField.text {
439446
guard let self else { return }
440447
self.saveNewList(userEnteredListName: text)
441-
if !getUserBlockedList().isEmpty {
442-
tableView.clear()
443-
}
448+
// if !getBlockedLists().isEmpty {
449+
// tableView.clear()
450+
// }
444451
self.reloadCustomBlockedLists()
445452
self.listsSubmenuView.isHidden = true
446453
}
@@ -500,8 +507,9 @@ extension BlockListViewController {
500507
style: .destructive,
501508
handler: { [weak self] (_) in
502509
guard let self else { return }
503-
deleteUserBlockedList(list: list)
510+
deleteBlockedList(listName: list)
504511
self.customBlockedListsTableView.clear()
512+
self.reloadCustomBlockedLists()
505513
}))
506514

507515
present(alert, animated: true, completion: nil)
@@ -564,8 +572,8 @@ extension BlockListViewController {
564572
}
565573

566574
@objc func editDomains() {
567-
let controller = EditDomainsViewController()
568-
present(controller, animated: true)
575+
let vc = EditDomainsViewController()
576+
navigationController?.pushViewController(vc, animated: true)
569577
}
570578

571579
func showSuccessAlert() {

‎LockdowniOS/CustomTableView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ final class CustomTableView: UITableView {
8585
var headerView = TableViewHeader()
8686

8787
override init(frame: CGRect, style: UITableView.Style) {
88-
super.init(frame: frame, style: .insetGrouped)
88+
super.init(frame: frame, style: .grouped)
8989
setup()
9090
}
9191

‎LockdowniOS/DomainsBlockedTableViewCell.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ final class DomainsBlockedTableViewCell: UITableViewCell {
5757

5858
override func prepareForReuse() {
5959
super.prepareForReuse()
60-
iconImageView.image = nil
60+
// iconImageView.image = nil
6161
label.text = nil
62-
statusLabel.text = nil
62+
// statusLabel.text = nil
6363
}
6464

6565
private func confugureUI() {

‎LockdowniOS/EditDomainsCell.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ extension EditDomainsCell {
7575
let title: String?
7676
let status: String?
7777

78-
static func userBlocked(domain: String, isUnselected: Bool) -> Contents {
79-
let checkMark = isUnselected ? UIImage(systemName: "circle") : UIImage(systemName: "checkmark.circle.fill")
78+
static func userBlocked(domain: String, isSelected: Bool) -> Contents {
79+
let checkMark = isSelected ? UIImage(systemName: "checkmark.circle.fill") : UIImage(systemName: "circle")
8080
let image = UIImage(named: "website_icon.png")
8181
let status = NSLocalizedString("Blocked", comment: "")
8282
return Contents(checkMark: checkMark, icon: image, title: domain, status: status)

‎LockdowniOS/EditDomainsViewController.swift

+28-20
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ final class EditDomainsViewController: UIViewController {
1212
// MARK: - Properties
1313
private var didMakeChange = false
1414

15+
private var checkedStatus = false
16+
1517
var customBlockedDomains: [(String, Bool)] = []
16-
var selectedBlockedDomains: [(String, Bool)] = []
18+
var selectedBlockedDomains: Dictionary<String, Bool> = [:]
1719

1820
private var titleName = NSLocalizedString("Edit Domains", comment: "")
1921

@@ -43,8 +45,6 @@ final class EditDomainsViewController: UIViewController {
4345

4446
private let customBlockedDomainsTableView = CustomTableView()
4547

46-
private let tableView = UITableView(frame: .zero, style: .insetGrouped)
47-
4848
// MARK: - Lifecycle
4949
override func viewDidLoad() {
5050
super.viewDidLoad()
@@ -68,8 +68,9 @@ final class EditDomainsViewController: UIViewController {
6868
}
6969

7070
private func configureDomainsTableView() {
71+
7172
addTableView(customBlockedDomainsTableView) { tableView in
72-
tableView.anchors.top.spacing(16, to: navigationView.anchors.bottom)
73+
tableView.anchors.top.spacing(24, to: navigationView.anchors.bottom)
7374
tableView.anchors.leading.pin()
7475
tableView.anchors.trailing.pin()
7576
}
@@ -111,23 +112,25 @@ private extension EditDomainsViewController {
111112
tableTitle.anchors.bottom.marginsPin()
112113
}
113114

114-
for (domain, status) in customBlockedDomains {
115-
var checkedStatus = status
115+
for (domain, _) in customBlockedDomains {
116116
let blockListView = EditDomainsCell()
117-
blockListView.contents = .userBlocked(domain: domain, isUnselected: status)
117+
blockListView.contents = .userBlocked(domain: domain, isSelected: checkedStatus)
118118

119+
self.selectedBlockedDomains[domain] = checkedStatus
119120
let cell = tableView.addRow { (contentView) in
120121
contentView.addSubview(blockListView)
121122
blockListView.anchors.edges.pin()
122-
}.onSelect { [unowned blockListView] in
123+
124+
}.onSelect { [unowned blockListView, unowned self] in
123125
self.didMakeChange = true
126+
124127
checkedStatus.toggle()
125-
blockListView.contents = .userBlocked(domain: domain, isUnselected: checkedStatus)
128+
blockListView.contents = .userBlocked(domain: domain, isSelected: checkedStatus)
129+
130+
self.selectedBlockedDomains[domain] = checkedStatus
126131

127132
self.bottomMenu.middleButton.setTitleColor(.tunnelsBlue, for: .normal)
128133
self.bottomMenu.rightButton.setTitleColor(.red, for: .normal)
129-
// TODO: - move domains to list
130-
131134
}
132135

133136
cell.accessoryType = .none
@@ -139,16 +142,16 @@ private extension EditDomainsViewController {
139142
}
140143

141144
@objc func selectAllddDomains() {
142-
let tableView = customBlockedDomainsTableView
143-
tableView.reloadData()
145+
checkedStatus = true
146+
reloadCustomBlockedDomains()
144147
}
145148

146149
@objc func moveToList() {
147-
// var arr: [(String, Bool)] = []
148-
// arr = selectedBlockedDomains.filter { (_, checked) in
149-
// checked == true
150-
// }
151-
// print(arr)
150+
let sortedDomains = selectedBlockedDomains.filter({ $0.value == true })
151+
152+
let vc = MoveToListViewController()
153+
vc.selectedDomains = sortedDomains
154+
present(vc, animated: true)
152155
}
153156

154157
@objc func deleteDomains() {
@@ -162,8 +165,13 @@ private extension EditDomainsViewController {
162165
style: UIAlertAction.Style.destructive,
163166
handler: { [weak self] (_) in
164167
guard let self else { return }
165-
self.customBlockedDomainsTableView.clear()
166-
self.customBlockedDomainsTableView.reloadData()
168+
let sortedDomains = self.selectedBlockedDomains.filter({ $0.value == true })
169+
170+
for domain in sortedDomains.keys {
171+
deleteUserBlockedDomain(domain: domain)
172+
}
173+
174+
self.reloadCustomBlockedDomains()
167175
}))
168176
self.present(alert, animated: true, completion: nil)
169177
}

‎LockdowniOS/ImportBlockListViewController.swift

+51-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
//
77

88
import UIKit
9+
import UniformTypeIdentifiers
910

10-
final class ImportBlockListViewController: UIViewController {
11+
final class ImportBlockListViewController: UIViewController, UIDocumentPickerDelegate {
1112

1213
// MARK: - Properties
1314

@@ -151,11 +152,57 @@ private extension ImportBlockListViewController {
151152
return documentsDirectory
152153
}
153154

155+
// list name validation code method
156+
func isValidListName(text: String) -> Bool {
157+
let regEx = "^[a-zA-Z0-9]{1,20}$"
158+
return text.range(of: "\(regEx)", options: .regularExpression) != nil
159+
}
160+
154161
@objc func selectFromFiles() {
155-
let path = getDocumentsDirectory().absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
156-
let url = URL(string: path)!
162+
163+
let alertController = UIAlertController(title: "Create New List", message: nil, preferredStyle: .alert)
164+
165+
let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] (_) in
166+
if let txtField = alertController.textFields?.first, let text = txtField.text {
167+
guard let self else { return }
168+
169+
addBlockedList(listName: text)
170+
let path = self.getDocumentsDirectory().absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
171+
let url = URL(string: path)!
172+
173+
UIApplication.shared.open(url)
174+
175+
}
176+
}
177+
178+
saveAction.isEnabled = false
179+
180+
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { [weak self] (_) in
181+
guard let self else { return }
182+
}
183+
184+
alertController.addTextField { (textField) in
185+
textField.placeholder = NSLocalizedString("List Name", comment: "")
186+
}
187+
188+
NotificationCenter.default.addObserver(
189+
forName: UITextField.textDidChangeNotification,
190+
object: alertController.textFields?.first,
191+
queue: .main) { (notification) -> Void in
192+
guard let textFieldText = alertController.textFields?.first?.text else { return }
193+
saveAction.isEnabled = self.isValidListName(text: textFieldText) && !textFieldText.isEmpty
194+
}
195+
196+
alertController.addAction(saveAction)
197+
alertController.addAction(cancelAction)
198+
present(alertController, animated: true, completion: nil)
199+
200+
201+
202+
203+
204+
157205

158-
UIApplication.shared.open(url)
159206
}
160207

161208
@objc func blockPastedDomains() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// JSONSerialization+Extentions.swift
3+
// Lockdown
4+
//
5+
// Created by Aliaksandr Dvoineu on 06.04.23.
6+
// Copyright © 2023 Confirmed Inc. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
extension JSONSerialization {
12+
13+
static func loadJSON(withFilename filename: String) throws -> Any? {
14+
let fm = FileManager.default
15+
let urls = fm.urls(for: .documentDirectory, in: .userDomainMask)
16+
if let url = urls.first {
17+
var fileURL = url.appendingPathComponent(filename)
18+
fileURL = fileURL.appendingPathExtension("json")
19+
let data = try Data(contentsOf: fileURL)
20+
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [.mutableContainers, .mutableLeaves])
21+
return jsonObject
22+
}
23+
return nil
24+
}
25+
26+
static func save(jsonObject: Any, toFilename filename: String) throws -> Bool{
27+
let fm = FileManager.default
28+
let urls = fm.urls(for: .documentDirectory, in: .userDomainMask)
29+
if let url = urls.first {
30+
var fileURL = url.appendingPathComponent(filename)
31+
fileURL = fileURL.appendingPathExtension("json")
32+
let data = try JSONSerialization.data(withJSONObject: jsonObject, options: [.prettyPrinted])
33+
try data.write(to: fileURL, options: [.atomicWrite])
34+
return true
35+
}
36+
37+
return false
38+
}
39+
}

‎LockdowniOS/ListDescriptionViewController.swift

+15-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import UIKit
1010

1111
final class ListDescriptionViewController: UIViewController {
1212

13+
var listName = ""
14+
15+
var domains = getBlockedLists().userBlockListsDefaults
16+
1317
// MARK: - Properties
1418
private lazy var navigationView: ConfiguredNavigationView = {
1519
let view = ConfiguredNavigationView()
@@ -26,6 +30,7 @@ final class ListDescriptionViewController: UIViewController {
2630
private lazy var descriptionTextField: UITextField = {
2731
let view = UITextField()
2832
view.placeholder = "No Description"
33+
view.text = domains[listName]?.description
2934
view.font = fontMedium17
3035
view.textColor = .label
3136
view.backgroundColor = .systemBackground
@@ -77,8 +82,17 @@ private extension ListDescriptionViewController {
7782
}
7883

7984
@objc func doneButtonClicked() {
85+
let domains = getBlockedLists().userBlockListsDefaults
86+
var userList = domains[listName]
87+
88+
userList?.description = descriptionTextField.text
89+
90+
var data = getBlockedLists()
91+
data.userBlockListsDefaults[listName] = userList
92+
let encodedData = try? JSONEncoder().encode(data)
93+
defaults.set(encodedData, forKey: kUserBlockedLists)
94+
8095
navigationController?.popViewController(animated: true)
81-
// TODO: - add new list name to defaults if changed
8296
}
8397

8498
@objc func dismissKeyboard() {

‎LockdowniOS/ListDetailViewController.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import UIKit
1010

1111
final class ListDetailViewController: UIViewController {
1212

13+
var listName = ""
14+
1315
// MARK: - Properties
1416
private lazy var navigationView: ConfiguredNavigationView = {
1517
let view = ConfiguredNavigationView()
@@ -25,7 +27,7 @@ final class ListDetailViewController: UIViewController {
2527

2628
private lazy var listNameTextField: UITextField = {
2729
let view = UITextField()
28-
view.text = "My List 1"
30+
view.text = listName
2931
view.font = fontMedium17
3032
view.textColor = .label
3133
view.backgroundColor = .systemBackground

‎LockdowniOS/ListSettingsViewController.swift

+76-13
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@ final class ListSettingsViewController: UIViewController {
1313

1414
// MARK: - Properties
1515

16+
var listName = ""
17+
var descriptionText = ""
18+
1619
var blockedList: UserBlockListsGroup?
1720

1821
weak var blockListVC: BlockListViewController?
1922

20-
var titleName = ""
21-
2223
var didMakeChange = false
2324

2425
lazy var navigationView: ConfiguredNavigationView = {
2526
let view = ConfiguredNavigationView()
26-
view.titleLabel.text = titleName
27+
view.titleLabel.text = listName
2728
view.leftNavButton.setTitle(NSLocalizedString("BACK", comment: ""), for: .normal)
2829
view.leftNavButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
2930
view.leftNavButton.addTarget(self, action: #selector(backButtonClicked), for: .touchUpInside)
@@ -36,13 +37,15 @@ final class ListSettingsViewController: UIViewController {
3637
private lazy var switchBlockingView: SwitchBlockingView = {
3738
let view = SwitchBlockingView()
3839
view.titleLabel.text = NSLocalizedString("Blocking", comment: "")
40+
view.switchView.addTarget(self, action: #selector(toggleBlocking), for: .valueChanged)
3941
return view
4042
}()
4143

4244
private lazy var subMenu: ListsSubmenuView = {
4345
let view = ListsSubmenuView()
4446
view.topButton.setTitle(NSLocalizedString("Export List...", comment: ""), for: .normal)
4547
view.topButton.setImage(UIImage(named: "icn_export_folder"), for: .normal)
48+
view.topButton.addTarget(self, action: #selector(exportList), for: .touchUpInside)
4649
view.bottomButton.setTitle(NSLocalizedString("Delete List...", comment: ""), for: .normal)
4750
view.bottomButton.setImage(UIImage(named: "icn_trash"), for: .normal)
4851
view.bottomButton.addTarget(self, action: #selector(deleteList), for: .touchUpInside)
@@ -55,6 +58,10 @@ final class ListSettingsViewController: UIViewController {
5558
override func viewDidLoad() {
5659
super.viewDidLoad()
5760
view.backgroundColor = .secondarySystemBackground
61+
if let list = blockedList {
62+
switchBlockingView.switchView.isOn = list.enabled
63+
}
64+
5865
configureUI()
5966
configureTableView()
6067
}
@@ -84,8 +91,9 @@ final class ListSettingsViewController: UIViewController {
8491
subMenu.anchors.trailing.marginsPin()
8592
subMenu.isHidden = true
8693

87-
// let tap = UITapGestureRecognizer(target: self, action: #selector(hideSubmenu))
88-
// view.addGestureRecognizer(tap)
94+
let tap = UITapGestureRecognizer(target: self, action: #selector(hideSubmenu))
95+
tap.cancelsTouchesInView = false
96+
view.addGestureRecognizer(tap)
8997
}
9098

9199
func configureTableView() {
@@ -144,10 +152,12 @@ extension ListSettingsViewController: UITableViewDataSource {
144152
}
145153

146154
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
155+
let userBlockedLists = getBlockedLists().userBlockListsDefaults
156+
let numberOfDomains = userBlockedLists[listName]?.domains.count
147157

148158
switch section {
149159
case 0, 1: return 1
150-
case 2: return getUserBlockedDomains().count
160+
case 2: return numberOfDomains ?? 0
151161
default: return 0
152162
}
153163
}
@@ -157,27 +167,29 @@ extension ListSettingsViewController: UITableViewDataSource {
157167
}
158168

159169
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
160-
161-
let domains = getUserBlockedDomains()
170+
let userBlockedLists = getBlockedLists().userBlockListsDefaults
171+
let domains = userBlockedLists[listName]?.domains
162172

163173
switch indexPath.section {
164174
case 0:
165175
guard let cell = tableView.dequeueReusableCell(withIdentifier: ListBlockedTableViewCell.identifier, for: indexPath) as? ListBlockedTableViewCell else {
166176
return UITableViewCell()
167177
}
168-
cell.label.text = title
178+
cell.label.text = userBlockedLists[listName]?.name
169179
return cell
170180
case 1:
171181
guard let cell = tableView.dequeueReusableCell(withIdentifier: ListBlockedTableViewCell.identifier, for: indexPath) as? ListBlockedTableViewCell else {
172182
return UITableViewCell()
173183
}
174-
cell.label.text = "Description"
184+
185+
cell.label.text = userBlockedLists[listName]?.description ?? "Description"
186+
175187
return cell
176188
case 2:
177189
guard let cell = tableView.dequeueReusableCell(withIdentifier: DomainsBlockedTableViewCell.identifier, for: indexPath) as? DomainsBlockedTableViewCell else {
178190
return UITableViewCell()
179191
}
180-
cell.label.text = "reroi.com"
192+
cell.label.text = domains?[indexPath.row].name
181193
return cell
182194
default:
183195
return UITableViewCell()
@@ -189,9 +201,11 @@ extension ListSettingsViewController: UITableViewDataSource {
189201
switch indexPath.section {
190202
case 0:
191203
let vc = ListDetailViewController()
204+
vc.listName = listName
192205
navigationController?.pushViewController(vc, animated: true)
193206
case 1:
194207
let vc = ListDescriptionViewController()
208+
vc.listName = listName
195209
navigationController?.pushViewController(vc, animated: true)
196210
default:
197211
break
@@ -213,7 +227,13 @@ extension ListSettingsViewController {
213227
func saveNewDomain(userEnteredDomainName: String) {
214228
didMakeChange = true
215229
DDLogInfo("Adding custom domain - \(userEnteredDomainName)")
216-
addUserBlockedDomain(domain: userEnteredDomainName.lowercased())
230+
231+
let domains = getBlockedLists().userBlockListsDefaults
232+
let userList = domains[listName]
233+
234+
if let list = userList {
235+
addDomainToBlockedList(domain: userEnteredDomainName, for: list.name.lowercased())
236+
}
217237
tableView.reloadData()
218238
}
219239

@@ -253,8 +273,51 @@ extension ListSettingsViewController {
253273
style: UIAlertAction.Style.destructive,
254274
handler: { [weak self] (_) in
255275
guard let self else { return }
256-
self.tableView.reloadData()
276+
if let vc = self.blockListVC {
277+
vc.didMakeChange = true
278+
}
279+
280+
281+
if let list = self.blockedList {
282+
deleteBlockedList(listName: list.name)
283+
}
284+
self.backButtonClicked()
257285
}))
258286
self.present(alert, animated: true, completion: nil)
259287
}
288+
289+
@objc func toggleBlocking(sender: UISwitch) {
290+
let sender = switchBlockingView.switchView
291+
setBlockingEnabled(sender.isOn)
292+
}
293+
294+
func setBlockingEnabled(_ isEnabled: Bool) {
295+
if let vc = self.blockListVC {
296+
vc.didMakeChange = true
297+
}
298+
299+
let domains = getBlockedLists().userBlockListsDefaults
300+
var userList = domains[listName]
301+
302+
userList?.enabled = isEnabled
303+
304+
var data = getBlockedLists()
305+
data.userBlockListsDefaults[listName] = userList
306+
let encodedData = try? JSONEncoder().encode(data)
307+
defaults.set(encodedData, forKey: kUserBlockedLists)
308+
}
309+
310+
@objc func exportList(_ sender: UIButton) {
311+
let domains = getBlockedLists().userBlockListsDefaults
312+
var domainsList = domains[listName]?.domains
313+
314+
guard let url = domainsList?.first?.exportToURL() else { return }
315+
316+
let activity = UIActivityViewController(
317+
activityItems: ["Export your domains", url],
318+
applicationActivities: nil
319+
)
320+
activity.popoverPresentationController?.sourceView = sender
321+
present(activity, animated: true, completion: nil)
322+
}
260323
}

‎LockdowniOS/MoveToLsisViewController.swift ‎LockdowniOS/MoveToListViewController.swift

+27-15
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88
import UIKit
99
import CocoaLumberjackSwift
1010

11-
final class MoveToLsisViewController: UIViewController {
11+
final class MoveToListViewController: UIViewController {
1212

1313
// MARK: - Properties
1414
private var didMakeChange = false
1515

16-
private var customBlockedDomains: [(String, Bool)] = [] {
16+
var selectedDomains: Dictionary<String, Bool> = [:] {
1717
didSet {
18-
let domains = getUserBlockedDomains()
19-
numberOfdomains.text = NSLocalizedString("\(domains.count) domains", comment: "")
20-
domainsList.text = domains.map(\.0).joined(separator: ", ")
18+
if selectedDomains.count == 1 {
19+
numberOfdomains.text = "\(selectedDomains.count) " + NSLocalizedString("domain", comment: "")
20+
} else {
21+
numberOfdomains.text = "\(selectedDomains.count) " + NSLocalizedString("domains", comment: "")
22+
}
23+
domainsList.text = selectedDomains.map(\.0).joined(separator: ", ")
2124
}
2225
}
2326

@@ -141,12 +144,12 @@ final class MoveToLsisViewController: UIViewController {
141144
}
142145

143146
// MARK: - Private functions
144-
private extension MoveToLsisViewController {
147+
private extension MoveToListViewController {
145148

146149
func reloadCustomBlockedLists() {
147150
customBlockedListsTableView.clear()
148151
customBlockedLists = {
149-
let lists = getUserBlockedLists().userBlockListsDefaults
152+
let lists = getBlockedLists().userBlockListsDefaults
150153
let sorted = lists.sorted(by: { $0.key < $1.key })
151154
return Array(sorted.map(\.value))
152155
}()
@@ -156,6 +159,9 @@ private extension MoveToLsisViewController {
156159
}
157160

158161
func createUserBlockedListsRows() {
162+
let userBlockedLists = getBlockedLists().userBlockListsDefaults
163+
164+
159165
let tableView = customBlockedListsTableView
160166
tableView.separatorStyle = .singleLine
161167

@@ -178,8 +184,16 @@ private extension MoveToLsisViewController {
178184
let cell = tableView.addRow { (contentView) in
179185
contentView.addSubview(blockListView)
180186
blockListView.anchors.edges.pin()
181-
}.onSelect { [unowned blockListView] in
187+
}.onSelect { [unowned self] in
182188
self.didMakeChange = true
189+
190+
let blockedList = userBlockedLists[list.name]
191+
192+
if let blockedList = blockedList {
193+
for domain in self.selectedDomains.keys {
194+
addDomainToBlockedList(domain: domain, for: blockedList.name)
195+
}
196+
}
183197
}
184198

185199
cell.accessoryType = .none
@@ -188,7 +202,7 @@ private extension MoveToLsisViewController {
188202

189203
func saveNewList(userEnteredListName: String) {
190204
DDLogInfo("Adding custom list - \(userEnteredListName)")
191-
addUserBlockedList(list: userEnteredListName.lowercased())
205+
addBlockedList(listName: userEnteredListName)
192206
reloadCustomBlockedLists()
193207
}
194208

@@ -220,15 +234,13 @@ private extension MoveToLsisViewController {
220234
if let txtField = alertController.textFields?.first, let text = txtField.text {
221235
guard let self else { return }
222236
self.saveNewList(userEnteredListName: text)
223-
if !getUserBlockedList().isEmpty {
224-
tableView.clear()
225-
}
237+
// if !getBlockedLists().isEmpty {
238+
// tableView.clear()
239+
// }
226240
self.reloadCustomBlockedLists()
227241
}
228242
}
229-
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { [weak self] (_) in
230-
guard let self else { return }
231-
}
243+
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
232244
alertController.addTextField { (textField) in
233245
textField.placeholder = NSLocalizedString("List Name", comment: "")
234246
}

‎LockdowniOS/SwitchBlockingView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class SwitchBlockingView: UIView {
3232
lazy var switchView: UISwitch = {
3333
let view = UISwitch()
3434
view.onTintColor = .tunnelsBlue
35-
view.isOn = true
35+
// view.isOn = true
3636
return view
3737
}()
3838

0 commit comments

Comments
 (0)
Please sign in to comment.