Recordings interface

This commit is contained in:
relikd
2020-04-02 18:28:20 +02:00
parent 144773ddaa
commit 79f836016a
13 changed files with 690 additions and 98 deletions

View File

@@ -1,63 +1,67 @@
import UIKit
// MARK: Basic Alerts
/// - Parameters:
/// - buttonText: Default: "Dismiss"
func Alert(title: String?, text: String?, buttonText: String = "Dismiss") -> UIAlertController {
let alert = UIAlertController(title: title, message: text, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: buttonText, style: .cancel, handler: nil))
return alert
}
/// - Parameters:
/// - buttonText: Default: "Dismiss"
func ErrorAlert(_ error: Error, buttonText: String = "Dismiss") -> UIAlertController {
return Alert(title: "Error", text: error.localizedDescription, buttonText: buttonText)
}
/// - Parameters:
/// - buttonText: Default: "Dismiss"
func ErrorAlert(_ errorDescription: String, buttonText: String = "Dismiss") -> UIAlertController {
return Alert(title: "Error", text: errorDescription, buttonText: buttonText)
}
/// - Parameters:
/// - buttonText: Default: "Continue"
/// - buttonStyle: Default: `.default`
func AskAlert(title: String?, text: String?, buttonText: String = "Continue", buttonStyle: UIAlertAction.Style = .default, action: @escaping (UIAlertController) -> Void) -> UIAlertController {
let alert = Alert(title: title, text: text, buttonText: "Cancel")
alert.addAction(UIAlertAction(title: buttonText, style: buttonStyle) { _ in action(alert) })
return alert
}
extension UIAlertController {
func presentIn(_ viewController: UIViewController?) {
viewController?.present(self, animated: true, completion: nil)
}
}
// MARK: Basic Alerts
/// - Parameters:
/// - buttonText: Default: `"Dismiss"`
func Alert(title: String?, text: String?, buttonText: String = "Dismiss") -> UIAlertController {
let alert = UIAlertController(title: title, message: text, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: buttonText, style: .cancel, handler: nil))
return alert
}
/// - Parameters:
/// - buttonText: Default:`"Dismiss"`
func ErrorAlert(_ error: Error, buttonText: String = "Dismiss") -> UIAlertController {
return Alert(title: "Error", text: error.localizedDescription, buttonText: buttonText)
}
/// - Parameters:
/// - buttonText: Default: `"Dismiss"`
func ErrorAlert(_ errorDescription: String, buttonText: String = "Dismiss") -> UIAlertController {
return Alert(title: "Error", text: errorDescription, buttonText: buttonText)
}
/// - Parameters:
/// - buttonText: Default: `"Continue"`
/// - buttonStyle: Default: `.default`
func AskAlert(title: String?, text: String?, buttonText: String = "Continue", buttonStyle: UIAlertAction.Style = .default, action: @escaping (UIAlertController) -> Void) -> UIAlertController {
let alert = Alert(title: title, text: text, buttonText: "Cancel")
alert.addAction(UIAlertAction(title: buttonText, style: buttonStyle) { _ in action(alert) })
return alert
}
// MARK: Alert with multiple options
func AlertWithOptions(title: String?, text: String?, buttons: [String], lastIsDestructive: Bool = false, callback: @escaping (_ index: Int?) -> Void) -> UIAlertController {
/// - Parameters:
/// - buttons: Default: `[]`
/// - lastIsDestructive: Default: `false`
/// - cancelButtonText: Default: `"Dismiss"`
func BottomAlert(title: String?, text: String?, buttons: [String] = [], lastIsDestructive: Bool = false, cancelButtonText: String = "Dismiss", callback: @escaping (_ index: Int?) -> Void) -> UIAlertController {
let alert = UIAlertController(title: title, message: text, preferredStyle: .actionSheet)
for (i, btn) in buttons.enumerated() {
let dangerous = (lastIsDestructive && i + 1 == buttons.count)
alert.addAction(UIAlertAction(title: btn, style: dangerous ? .destructive : .default) { _ in callback(i) })
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in callback(nil) })
alert.addAction(UIAlertAction(title: cancelButtonText, style: .cancel) { _ in callback(nil) })
return alert
}
func AlertDeleteLogs(_ domain: String, latest: Timestamp, success: @escaping (_ tsMin: Timestamp) -> Void) -> UIAlertController {
let sinceNow = TimestampNow() - latest
let sinceNow = Timestamp.now() - latest
var buttons = ["Last 5 minutes", "Last 15 minutes", "Last hour", "Last 24 hours", "Delete everything"]
var times: [Timestamp] = [300, 900, 3600, 86400]
while times.count > 0, times[0] < sinceNow {
buttons.removeFirst()
times.removeFirst()
}
return AlertWithOptions(title: "Delete logs", text: "Delete logs for domain '\(domain)'", buttons: buttons, lastIsDestructive: true) {
return BottomAlert(title: "Delete logs", text: "Delete logs for domain '\(domain)'", buttons: buttons, lastIsDestructive: true, cancelButtonText: "Cancel") {
guard let idx = $0 else {
return
}

View File

@@ -18,3 +18,19 @@ extension Array where Element == GroupedDomain {
return GroupedDomain(domain: domain, total: t, blocked: b, lastModified: m, options: opt)
}
}
extension Recording {
func stoppedCopy() -> Recording {
stop != nil ? self : Recording(start: start, stop: Timestamp(Date().timeIntervalSince1970),
appId: appId, title: title, notes: notes)
}
var fallbackTitle: String { get { "Unnamed #\(start)" } }
var duration: Timestamp? { get { stop == nil ? nil : stop! - start } }
var durationString: String? { get { stop == nil ? nil : TimeFormat.from(duration!) } }
}
extension Timestamp {
func asDateTime() -> String { dateTimeFormat.string(from: self) }
func toDate() -> Date { Date(timeIntervalSince1970: Double(self)) }
static func now() -> Timestamp { Timestamp(Date().timeIntervalSince1970) }
}

View File

@@ -84,4 +84,20 @@ extension DateFormatter {
}
}
func TimestampNow() -> Timestamp { Timestamp(Date().timeIntervalSince1970) }
struct TimeFormat {
static func from(_ duration: Timestamp) -> String {
String(format: "%02d:%02d", duration / 60, duration % 60)
}
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
let t = Int(duration)
if millis {
let mil = Int(duration * 1000) % 1000
return String(format: "%02d:%02d.%03d", t / 60, t % 60, mil)
}
return String(format: "%02d:%02d", t / 60, t % 60)
}
static func since(_ date: Date, millis: Bool = false) -> String {
from(Date().timeIntervalSince(date), millis: millis)
}
}

View File

@@ -3,6 +3,7 @@ import Foundation
let NotifyVPNStateChanged = NSNotification.Name("GlassVPNStateChanged") // VPNState!
let NotifyFilterChanged = NSNotification.Name("PSIFilterSettingsChanged") // nil!
let NotifyLogHistoryReset = NSNotification.Name("PSILogHistoryReset") // nil!
let NotifyRecordingChanged = NSNotification.Name("PSIRecordingChanged") // (Recording, deleted: Bool)!
extension NSNotification.Name {

View File

@@ -3,8 +3,8 @@ import UIKit
extension GroupedDomain {
var detailCellText: String { get {
return blocked > 0
? "\(dateTimeFormat.string(from: lastModified))\(blocked)/\(total) blocked"
: "\(dateTimeFormat.string(from: lastModified))\(total)"
? "\(lastModified.asDateTime())\(blocked)/\(total) blocked"
: "\(lastModified.asDateTime())\(total)"
}
}
}
@@ -59,29 +59,29 @@ extension IncrementalDataSourceUpdate {
func insertRow(_ obj: GroupedDomain, at index: Int) {
dataSource.insert(obj, at: index)
ifDisplayed {
self.tableView.insertRows(at: [IndexPath(row: index, section: 0)], with: .left)
self.tableView.insertRows(at: [IndexPath(row: index)], with: .left)
}
}
func moveRow(_ obj: GroupedDomain, from: Int, to: Int) {
dataSource.remove(at: from)
dataSource.insert(obj, at: to)
ifDisplayed {
let source = IndexPath(row: from, section: 0)
let source = IndexPath(row: from)
let cell = self.tableView.cellForRow(at: source)
cell?.detailTextLabel?.text = obj.detailCellText
self.tableView.moveRow(at: source, to: IndexPath(row: to, section: 0))
self.tableView.moveRow(at: source, to: IndexPath(row: to))
}
}
func replaceRow(_ obj: GroupedDomain, at index: Int) {
dataSource[index] = obj
ifDisplayed {
self.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
self.tableView.reloadRows(at: [IndexPath(row: index)], with: .automatic)
}
}
func deleteRow(at index: Int) {
dataSource.remove(at: index)
ifDisplayed {
self.tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
self.tableView.deleteRows(at: [IndexPath(row: index)], with: .automatic)
}
}
func replaceData(with newData: [GroupedDomain]) {
@@ -91,3 +91,8 @@ extension IncrementalDataSourceUpdate {
}
}
}
extension IndexPath {
/// Convenience init with `section: 0`
public init(row: Int) { self.init(row: row, section: 0) }
}