Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
10aa9a9
get new iOS target in place
amiantos May 30, 2020
068a648
move shared files to a separate group and get building PiholeAPI to w…
amiantos May 30, 2020
a078fe9
mysterious storyboard changes
amiantos May 30, 2020
8dd099c
lower iOS deployment target to 11.4
amiantos May 30, 2020
03719d6
fix issues with scenedelegate stuff for iOS version <13
amiantos May 30, 2020
119788a
get rid of tab bar controller (don't think I'll need it)
amiantos May 30, 2020
f915e10
rudimentary network overview view is in place
amiantos Jun 3, 2020
1c7b670
disable landscape on iphone
amiantos Jun 3, 2020
53d7a59
add settings icon
amiantos Jun 3, 2020
96289ce
better corner rounding
amiantos Jun 3, 2020
62ca025
stack views make life better
amiantos Jun 3, 2020
93aa419
add some placeholder views
amiantos Jun 3, 2020
aa8cda3
some stack view fixes
amiantos Jun 4, 2020
64788c0
got some charting in place
amiantos Jun 4, 2020
aa84037
very rough data normalization
amiantos Jun 4, 2020
95d20cc
add some icons
amiantos Jun 4, 2020
0d20f91
get more charts in place
amiantos Jun 4, 2020
660761f
refactor charts
amiantos Jun 5, 2020
7ab85ef
make swiftlint happy
amiantos Jun 5, 2020
ba8dc9d
reorganize files
amiantos Jun 5, 2020
a3056a6
get some preferences UI in place to get wired up
amiantos Jun 9, 2020
f15a9d1
reconfigure initial view state to be initializing state
amiantos Aug 19, 2020
adc7bfb
get some ui hooked up
amiantos Aug 19, 2020
c1adb8e
swiftformat
amiantos Aug 19, 2020
c287bdf
new swiftformat
amiantos Aug 19, 2020
193f009
deselect button rows automatically
amiantos Aug 19, 2020
5e16cd9
adding pi-holes works (it seems)
amiantos Aug 21, 2020
3449faf
wire up cancel button to dismiss view
amiantos Aug 21, 2020
a814934
move stuff around in storyboard and silence swiftlint
amiantos Aug 22, 2020
8d59862
silence a lot of warnings
amiantos Aug 22, 2020
062d905
properly position SSL actionsheet on iPad
amiantos Aug 23, 2020
431dba7
experimenting with expanded readme
amiantos Aug 23, 2020
2303709
formatting changes to README
amiantos Aug 23, 2020
4ff68db
remove table, looked weird
amiantos Aug 23, 2020
2a7dd61
remove line separators
amiantos Aug 23, 2020
b77be0d
update my own credit
amiantos Aug 23, 2020
04abc04
some more formatting changes
amiantos Aug 23, 2020
6665ee2
start to write up preferences screen
amiantos Aug 25, 2020
322cfce
implement dynamic interface color
amiantos Aug 26, 2020
e489a0f
swiftformat
amiantos Aug 26, 2020
3be6167
appease the IB warning gods
amiantos Aug 26, 2020
60d41b8
update pods and resolve xcode warnings
amiantos Oct 26, 2020
f02cff0
implement 'normalize charts' in preferences screen
amiantos Oct 26, 2020
420d0c9
change display name and update preference label
amiantos Oct 26, 2020
eed64cc
add email option to preferences screen
amiantos Oct 26, 2020
53e64ed
run latest swiftformat
amiantos Oct 26, 2020
81ed5af
add polling rate preference screens
amiantos Oct 27, 2020
882dad0
wire up simple default disable duration menu
amiantos Nov 3, 2020
e5e3c72
run swiftformat
amiantos Nov 3, 2020
c0fbe20
add placeholder view for family mode preference
amiantos Nov 3, 2020
ecebd09
beginning of implementing disable menu
amiantos Nov 3, 2020
07446ea
implement disable button UI
amiantos Nov 3, 2020
28d20a4
run swiftformat
amiantos Nov 3, 2020
aa2b09a
add getAllQueries support to PiholeAPI class
amiantos Nov 4, 2020
bc64124
remove unneeded segment control from query log UI
amiantos Nov 4, 2020
3aa3e8b
add more placeholder UI
amiantos Nov 4, 2020
8867902
add some icon sizes
amiantos Dec 8, 2020
1138f1f
add missing ipad sized icons
amiantos Dec 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.0
5.3
3 changes: 2 additions & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
disabled_rules:
- trailing_comma
- trailing_comma # swiftformat is not copacetic
- opening_brace # swiftformat is not copacetic
opt_in_rules:
- empty_count
- empty_string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
import ServiceManagement

public struct LaunchAtLogin {
public enum LaunchAtLogin {
private static let id = "\(Bundle.main.bundleIdentifier!)-LaunchAtLoginHelper"

public static var isEnabled: Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
// 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 Cocoa
#if os(macOS)
import Cocoa
#elseif os(iOS)
import UIKit
#endif

class PiholeAPI: NSObject {
final class PiholeAPI {
let connection: PiholeConnectionV2

var identifier: String {
return "\(connection.hostname)"
"\(connection.hostname)"
}

private let path: String = "/admin/api.php"
Expand All @@ -29,9 +33,11 @@ class PiholeAPI: NSObject {
static let enable = PiholeAPIEndpoint(queryParameter: "enable", authorizationRequired: true)
static let disable = PiholeAPIEndpoint(queryParameter: "disable", authorizationRequired: true)
static let recentBlocked = PiholeAPIEndpoint(queryParameter: "recentBlocked", authorizationRequired: false)
static let getAllQueries = PiholeAPIEndpoint(queryParameter: "getAllQueries", authorizationRequired: true)

}

override init() {
init() {
connection = PiholeConnectionV2(
hostname: "pi.hole",
port: 80,
Expand All @@ -40,12 +46,10 @@ class PiholeAPI: NSObject {
passwordProtected: true,
adminPanelURL: "http://pi.hole/admin/"
)
super.init()
}

init(connection: PiholeConnectionV2) {
self.connection = connection
super.init()
}

private func get(_ endpoint: PiholeAPIEndpoint, argument: String? = nil, completion: @escaping (String?) -> Void) {
Expand Down Expand Up @@ -98,6 +102,7 @@ class PiholeAPI: NSObject {
let object = try jsonDecoder.decode(T.self, from: jsonData)
return object
} catch {
Log.debug(error.localizedDescription)
return nil
}
}
Expand All @@ -110,7 +115,7 @@ class PiholeAPI: NSObject {
}

var admin: URL {
return URL(string: "http://\(connection.hostname):\(connection.port)/admin")!
URL(string: "http://\(connection.hostname):\(connection.port)/admin")!
}

// MARK: - Testing
Expand All @@ -137,7 +142,7 @@ class PiholeAPI: NSObject {
DispatchQueue.global(qos: .background).async {
self.get(Endpoints.summary) { string in
guard let jsonString = string,
let summary: PiholeAPISummary = self.decodeJSON(jsonString) else { return completion(nil) }
let summary: PiholeAPISummary = self.decodeJSON(jsonString) else { return completion(nil) }
completion(summary)
}
}
Expand All @@ -153,6 +158,27 @@ class PiholeAPI: NSObject {
}
}

func fetchOverTimeData(completion: @escaping (PiholeOverTimeData?) -> Void) {
DispatchQueue.global(qos: .background).async {
self.get(Endpoints.overTimeData10mins) { string in
guard let jsonString = string,
let overTimeData: PiholeOverTimeData = self.decodeJSON(jsonString) else { return completion(nil) }
completion(overTimeData)
}
}
}

func fetchQueries(count: Int = 100, completion: @escaping (PiholeQueriesData?) -> Void) {
DispatchQueue.global(qos: .background).async {
let countString = String(count)
self.get(Endpoints.getAllQueries, argument: countString) { (string) in
guard let jsonString = string,
let allQueriesData: PiholeQueriesData = self.decodeJSON(jsonString) else { return completion(nil) }
completion(allQueriesData)
}
}
}

func disable(seconds: Int? = nil, completion: @escaping (Bool) -> Void) {
DispatchQueue.global(qos: .background).async {
var secondsString: String?
Expand All @@ -161,7 +187,7 @@ class PiholeAPI: NSObject {
}
self.get(Endpoints.disable, argument: secondsString) { string in
guard let jsonString = string,
let _: PiholeAPIStatus = self.decodeJSON(jsonString) else { return completion(false) }
let _: PiholeAPIStatus = self.decodeJSON(jsonString) else { return completion(false) }
completion(true)
}
}
Expand All @@ -171,7 +197,7 @@ class PiholeAPI: NSObject {
DispatchQueue.global(qos: .background).async {
self.get(Endpoints.enable) { string in
guard let jsonString = string,
let _: PiholeAPIStatus = self.decodeJSON(jsonString) else { return completion(false) }
let _: PiholeAPIStatus = self.decodeJSON(jsonString) else { return completion(false) }
completion(true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct Preferences {
static let verboseLabels = "verboseLabels"
static let shortcutEnabled = "shortcutEnabled"
static let pollingRate = "pollingRate"
static let interfaceColor = "interfaceColor"
static let normalizeCharts = "normalizeCharts"
static let defaultDisableDuration = "defaultDisableDuration"
}

static var standard: UserDefaults {
Expand All @@ -36,6 +39,9 @@ struct Preferences {
Key.verboseLabels: false,
Key.shortcutEnabled: true,
Key.pollingRate: 3,
Key.interfaceColor: "red",
Key.normalizeCharts: true,
Key.defaultDisableDuration: -1,
])

return database
Expand Down Expand Up @@ -94,47 +100,47 @@ extension UserDefaults {
}

var showBlocked: Bool {
return bool(forKey: Preferences.Key.showBlocked)
bool(forKey: Preferences.Key.showBlocked)
}

func set(showBlocked: Bool) {
set(showBlocked, for: Preferences.Key.showBlocked)
}

var showQueries: Bool {
return bool(forKey: Preferences.Key.showQueries)
bool(forKey: Preferences.Key.showQueries)
}

func set(showQueries: Bool) {
set(showQueries, for: Preferences.Key.showQueries)
}

var showPercentage: Bool {
return bool(forKey: Preferences.Key.showPercentage)
bool(forKey: Preferences.Key.showPercentage)
}

func set(showPercentage: Bool) {
set(showPercentage, for: Preferences.Key.showPercentage)
}

var showLabels: Bool {
return bool(forKey: Preferences.Key.showLabels)
bool(forKey: Preferences.Key.showLabels)
}

func set(showLabels: Bool) {
set(showLabels, for: Preferences.Key.showLabels)
}

var verboseLabels: Bool {
return bool(forKey: Preferences.Key.verboseLabels)
bool(forKey: Preferences.Key.verboseLabels)
}

func set(verboseLabels: Bool) {
set(verboseLabels, for: Preferences.Key.verboseLabels)
}

var shortcutEnabled: Bool {
return bool(forKey: Preferences.Key.shortcutEnabled)
bool(forKey: Preferences.Key.shortcutEnabled)
}

func set(shortcutEnabled: Bool) {
Expand All @@ -154,15 +160,40 @@ extension UserDefaults {
set(pollingRate, for: Preferences.Key.pollingRate)
}

var interfaceColor: String {
string(forKey: Preferences.Key.interfaceColor) ?? "red"
}

func set(interfaceColor: String) {
set(interfaceColor, for: Preferences.Key.interfaceColor)
}

var normalizeCharts: Bool {
bool(forKey: Preferences.Key.normalizeCharts)
}

func set(normalizeCharts: Bool) {
set(normalizeCharts, for: Preferences.Key.normalizeCharts)
}

var defaultDisableDuration: Int {
integer(forKey: Preferences.Key.defaultDisableDuration)
}

func set(defaultDisableDuration: Int) {
set(defaultDisableDuration, for: Preferences.Key.defaultDisableDuration)
}

// Helpers

var showTitle: Bool {
return showQueries || showBlocked || showPercentage
showQueries || showBlocked || showPercentage
}
}

private extension UserDefaults {
func set(_ object: Any?, for key: String) {
Log.debug("Saving \(String(describing: object)) to \(key)")
set(object, forKey: key)
synchronize()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ struct PiholeAPIStatus: Decodable {
let status: String
}

struct PiholeOverTimeData: Decodable {
let domainsOverTime: [String: Int]
let adsOverTime: [String: Int]
}

struct PiholeQueriesData: Decodable {
let data: [[String]]
}

// MARK: - Pi-hole Network

enum PiholeNetworkStatus: String {
Expand All @@ -127,6 +136,7 @@ struct Pihole {
let identifier: String
let online: Bool
let summary: PiholeAPISummary?
let overTimeData: PiholeOverTimeData?
let canBeManaged: Bool?
let enabled: Bool?

Expand All @@ -152,5 +162,13 @@ struct PiholeNetworkOverview {
let adsPercentageToday: Double
let averageBlocklist: Int

let overTimeData: PiholeNetworkOverTimeData?

let piholes: [String: Pihole]
}

struct PiholeNetworkOverTimeData {
let overview: [Double: (Double, Double)]
let maximumValue: Double
let piholes: [String: [Double: (Double, Double)]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class AsyncOperation: Operation {
}

override var isAsynchronous: Bool {
return true
true
}

var state = State.isReady {
Expand All @@ -32,11 +32,11 @@ class AsyncOperation: Operation {
}

override var isExecuting: Bool {
return state == .isExecuting
state == .isExecuting
}

override var isFinished: Bool {
return state == .isFinished
state == .isFinished
}

override func start() {
Expand Down
76 changes: 76 additions & 0 deletions PiBar Shared/Manager/Operations/UpdatePiholeOperation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// UpdatePiholeOperation.swift
// PiBar
//
// Created by Brad Root on 5/26/20.
// Copyright © 2020 Brad Root. 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 Foundation

final class UpdatePiholeOperation: AsyncOperation {
private(set) var pihole: Pihole

init(_ pihole: Pihole) {
self.pihole = pihole
}

override func main() {
Log.debug("Updating Pi-hole: \(pihole.identifier)")

var enabled: Bool? = true
var online = true
var canBeManaged: Bool = false

var receivedSummary: PiholeAPISummary?
var receivedOverTimeData: PiholeOverTimeData?

let group = DispatchGroup()

group.enter()
pihole.api.fetchSummary { summary in
Log.debug("Received Summary for \(self.pihole.identifier)")
receivedSummary = summary
group.leave()
}

group.enter()
pihole.api.fetchOverTimeData { overTimeData in
Log.debug("Received Over Time Data for \(self.pihole.identifier)")
receivedOverTimeData = overTimeData
group.leave()
}

group.wait()

if let summary = receivedSummary {
if summary.status != "enabled" {
enabled = false
}
if !pihole.api.connection.token.isEmpty || !pihole.api.connection.passwordProtected {
canBeManaged = true
}
} else {
enabled = nil
online = false
canBeManaged = false
}

let updatedPihole = Pihole(
api: pihole.api,
identifier: pihole.api.identifier,
online: online,
summary: receivedSummary,
overTimeData: receivedOverTimeData,
canBeManaged: canBeManaged,
enabled: enabled
)

pihole = updatedPihole

state = .isFinished
}
}
Loading