DatePickerAlert + DateFormat
This commit is contained in:
@@ -135,6 +135,8 @@
|
||||
54E540F4247D3F2600F7C34A /* TestDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E540F3247D3F2600F7C34A /* TestDataSource.swift */; };
|
||||
54E540F8247DB90F00F7C34A /* RecordingsDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E540F7247DB90F00F7C34A /* RecordingsDB.swift */; };
|
||||
54E540FA2482414800F7C34A /* SyncUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E540F92482414800F7C34A /* SyncUpdate.swift */; };
|
||||
54EFA4E6248EEE240022D618 /* DatePickerAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EFA4E5248EEE240022D618 /* DatePickerAlert.swift */; };
|
||||
54EFA4E82491A16A0022D618 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EFA4E72491A16A0022D618 /* Font.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -295,6 +297,8 @@
|
||||
54E540F3247D3F2600F7C34A /* TestDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDataSource.swift; sourceTree = "<group>"; };
|
||||
54E540F7247DB90F00F7C34A /* RecordingsDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingsDB.swift; sourceTree = "<group>"; };
|
||||
54E540F92482414800F7C34A /* SyncUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncUpdate.swift; sourceTree = "<group>"; };
|
||||
54EFA4E5248EEE240022D618 /* DatePickerAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerAlert.swift; sourceTree = "<group>"; };
|
||||
54EFA4E72491A16A0022D618 /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -418,6 +422,7 @@
|
||||
545DDDCE243E6267003B6544 /* TutorialSheet.swift */,
|
||||
54D8B979246C9F2000EB2414 /* FilterPipeline.swift */,
|
||||
54448A3124899A4000771C96 /* SearchBarManager.swift */,
|
||||
54EFA4E5248EEE240022D618 /* DatePickerAlert.swift */,
|
||||
);
|
||||
path = "Common Classes";
|
||||
sourceTree = "<group>";
|
||||
@@ -442,6 +447,7 @@
|
||||
54B345AA241BBA5B004C53CC /* AlertSheet.swift */,
|
||||
54448A2F248647D900771C96 /* Time.swift */,
|
||||
54751E502423955000168273 /* URL.swift */,
|
||||
54EFA4E72491A16A0022D618 /* Font.swift */,
|
||||
54448A2D2486464F00771C96 /* Array.swift */,
|
||||
54D8B97B2471A7E000EB2414 /* String.swift */,
|
||||
54B34595240F0513004C53CC /* TableView.swift */,
|
||||
@@ -837,6 +843,7 @@
|
||||
54D8B98624796E9900EB2414 /* GroupedDomainDataSource.swift in Sources */,
|
||||
54953E6F23E44CD00054345C /* TVCHostDetails.swift in Sources */,
|
||||
54D8B97C2471A7E000EB2414 /* String.swift in Sources */,
|
||||
54EFA4E6248EEE240022D618 /* DatePickerAlert.swift in Sources */,
|
||||
54C056DD23E9EEF700214A3F /* BundleIcon.swift in Sources */,
|
||||
542E2A982404973F001462DC /* TBCMain.swift in Sources */,
|
||||
54448A3224899A4000771C96 /* SearchBarManager.swift in Sources */,
|
||||
@@ -845,6 +852,7 @@
|
||||
545DDDD124436983003B6544 /* QuickUI.swift in Sources */,
|
||||
541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */,
|
||||
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */,
|
||||
54EFA4E82491A16A0022D618 /* Font.swift in Sources */,
|
||||
54D8B97A246C9F2000EB2414 /* FilterPipeline.swift in Sources */,
|
||||
54B34594240E6343004C53CC /* TVCFilter.swift in Sources */,
|
||||
54E540FA2482414800F7C34A /* SyncUpdate.swift in Sources */,
|
||||
|
||||
91
main/Common Classes/DatePickerAlert.swift
Normal file
91
main/Common Classes/DatePickerAlert.swift
Normal file
@@ -0,0 +1,91 @@
|
||||
import UIKit
|
||||
|
||||
class DatePickerAlert: UIViewController {
|
||||
|
||||
private var callback: (Date) -> Void
|
||||
private let picker: UIDatePicker = {
|
||||
let x = UIDatePicker()
|
||||
let h = x.sizeThatFits(.zero).height
|
||||
x.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: h)
|
||||
return x
|
||||
}()
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
@discardableResult required init(presentIn viewController: UIViewController, configure: ((UIDatePicker) -> Void)? = nil, onSuccess: @escaping (Date) -> Void) {
|
||||
callback = onSuccess
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
modalPresentationStyle = .custom
|
||||
if #available(iOS 13.0, *) {
|
||||
isModalInPresentation = true
|
||||
}
|
||||
presentIn(viewController, configure)
|
||||
}
|
||||
|
||||
internal override func loadView() {
|
||||
let cancel = QuickUI.button("Cancel", target: self, action: #selector(didTapCancel))
|
||||
let save = QuickUI.button("Save", target: self, action: #selector(didTapSave))
|
||||
save.titleLabel?.font = save.titleLabel?.font.bold()
|
||||
//cancel.setTitleColor(.systemRed, for: .normal)
|
||||
|
||||
let buttons = UIStackView(arrangedSubviews: [cancel, save])
|
||||
buttons.axis = .horizontal
|
||||
buttons.distribution = .fillEqually
|
||||
|
||||
let bg = UIView(frame: picker.frame)
|
||||
bg.frame.size.height += buttons.frame.height + 15
|
||||
bg.frame.origin.y = UIScreen.main.bounds.height - bg.frame.height - 15
|
||||
bg.backgroundColor = .sysBg
|
||||
bg.addSubview(picker)
|
||||
bg.addSubview(buttons)
|
||||
|
||||
let clearBg = UIView()
|
||||
clearBg.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
clearBg.addSubview(bg)
|
||||
|
||||
picker.anchor([.leading, .trailing, .top], to: bg)
|
||||
picker.bottomAnchor =&= buttons.topAnchor
|
||||
buttons.anchor([.leading, .trailing], to: bg)
|
||||
buttons.bottomAnchor =&= bg.bottomAnchor - 15
|
||||
bg.anchor([.leading, .trailing, .bottom], to: clearBg)
|
||||
|
||||
view = clearBg
|
||||
view.isHidden = true // otherwise picker will flash on present
|
||||
}
|
||||
|
||||
@objc private func didTapSave() {
|
||||
dismiss(animated: true) {
|
||||
self.callback(self.picker.date)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func didTapCancel() {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
private func presentIn(_ viewController: UIViewController, _ configure: ((UIDatePicker) -> Void)? = nil) {
|
||||
viewController.present(self, animated: false) {
|
||||
let control = self.view.subviews.first!
|
||||
let prev = control.frame.origin.y
|
||||
control.frame.origin.y += control.frame.height
|
||||
self.view.isHidden = false
|
||||
|
||||
configure?(self.picker)
|
||||
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
control.frame.origin.y = prev
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
let control = self.view.subviews.first!
|
||||
self.view.backgroundColor = .clear
|
||||
control.frame.origin.y += control.frame.height
|
||||
}) { _ in
|
||||
super.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,32 +39,3 @@ struct QuickUI {
|
||||
return txt
|
||||
}
|
||||
}
|
||||
|
||||
extension NSMutableAttributedString {
|
||||
static private var def: UIFont = .preferredFont(forTextStyle: .body)
|
||||
|
||||
func normal(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: .preferredFont(forTextStyle: style)) }
|
||||
func bold(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: UIFont.preferredFont(forTextStyle: style).bold()) }
|
||||
func italic(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: UIFont.preferredFont(forTextStyle: style).italic()) }
|
||||
|
||||
func h1(_ str: String) -> Self { normal(str, .title1) }
|
||||
func h2(_ str: String) -> Self { normal(str, .title2) }
|
||||
func h3(_ str: String) -> Self { normal(str, .title3) }
|
||||
|
||||
private func append(_ str: String, withFont: UIFont) -> Self {
|
||||
append(NSAttributedString(string: str, attributes: [
|
||||
.font : withFont,
|
||||
.foregroundColor : UIColor.sysFg
|
||||
]))
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension UIFont {
|
||||
func withTraits(traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
|
||||
UIFont(descriptor: fontDescriptor.withSymbolicTraits(traits)!, size: 0) // keep size as is
|
||||
}
|
||||
func bold() -> UIFont { withTraits(traits: .traitBold) }
|
||||
func italic() -> UIFont { withTraits(traits: .traitItalic) }
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class TutorialSheet: UIViewController, UIScrollViewDelegate {
|
||||
|
||||
let content = UIView()
|
||||
x.addSubview(content)
|
||||
content.translatesAutoresizingMaskIntoConstraints = false
|
||||
content.anchor([.left, .right, .top, .bottom], to: x)
|
||||
content.anchor([.width, .height], to: x) | .defaultLow
|
||||
return x
|
||||
@@ -62,7 +61,7 @@ class TutorialSheet: UIViewController, UIScrollViewDelegate {
|
||||
|
||||
// MARK: Init
|
||||
|
||||
required init?(coder: NSCoder) { super.init(coder: coder) }
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
required init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
@@ -98,7 +97,6 @@ class TutorialSheet: UIViewController, UIScrollViewDelegate {
|
||||
pager.numberOfPages += 1
|
||||
updateButtonTitle()
|
||||
let x = UIStackView(frame: pageScroll.bounds)
|
||||
x.translatesAutoresizingMaskIntoConstraints = false
|
||||
x.axis = .vertical
|
||||
x.backgroundColor = UIColor.black
|
||||
x.isOpaque = true
|
||||
@@ -125,8 +123,6 @@ class TutorialSheet: UIViewController, UIScrollViewDelegate {
|
||||
sheetBg.addSubview(pageScroll)
|
||||
sheetBg.addSubview(button)
|
||||
|
||||
for x in sheetBg.subviews { x.translatesAutoresizingMaskIntoConstraints = false }
|
||||
|
||||
pager.anchor([.top, .left, .right], to: sheetBg)
|
||||
pageScroll.topAnchor =&= pager.bottomAnchor
|
||||
pageScroll.anchor([.left, .right, .top, .bottom], to: sheetBg, margin: cornerRadius/2) | .defaultHigh
|
||||
|
||||
@@ -130,6 +130,13 @@ extension SQLiteDatabase {
|
||||
|
||||
// MARK: read
|
||||
|
||||
func dnsLogsMinDate() -> Timestamp? {
|
||||
try? run(sql:"SELECT min(ts) FROM heap") {
|
||||
try ifStep($0, SQLITE_ROW)
|
||||
return sqlite3_column_int64($0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Select min and max row id with given condition `ts >= ? AND ts < ?`
|
||||
/// - Returns: `nil` in case no rows are matching the condition
|
||||
func dnsLogsRowRange(between ts: Timestamp, and ts2: Timestamp) -> SQLiteRowRange? {
|
||||
|
||||
@@ -16,8 +16,8 @@ extension GroupedDomain {
|
||||
extension GroupedDomain {
|
||||
var detailCellText: String { get {
|
||||
return blocked > 0
|
||||
? "\(lastModified.asDateTime()) — \(blocked)/\(total) blocked"
|
||||
: "\(lastModified.asDateTime()) — \(total)"
|
||||
? "\(DateFormat.seconds(lastModified)) — \(blocked)/\(total) blocked"
|
||||
: "\(DateFormat.seconds(lastModified)) — \(total)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ extension UIView {
|
||||
private static let inverseItem: [NSLayoutConstraint.Attribute] = [.right, .bottom, .trailing, .lastBaseline, .rightMargin, .bottomMargin, .trailingMargin]
|
||||
|
||||
/// Create and active constraints for provided edges. Constraints will anchor the same edge on both `self` and `other`.
|
||||
/// - Note: Will set `translatesAutoresizingMaskIntoConstraints = false`
|
||||
/// - Parameters:
|
||||
/// - edges: List of constraint attributes, e.g. `[.top, .bottom, .left, .right]`
|
||||
/// - other: Instance to bind to, e.g. `UIView` or `UILayoutGuide`
|
||||
@@ -66,7 +67,8 @@ extension UIView {
|
||||
/// - rel: Constraint relation. (Default: `.equal`)
|
||||
/// - Returns: List of created and active constraints
|
||||
@discardableResult func anchor(_ edges: [NSLayoutConstraint.Attribute], to other: Any, margin: CGFloat = 0, if rel: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
|
||||
edges.map {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
return edges.map {
|
||||
let (A, B) = UIView.inverseItem.contains($0) ? (other, self) : (self, other)
|
||||
return NSLayoutConstraint(item: A, attribute: $0, relatedBy: rel, toItem: B, attribute: $0, multiplier: 1, constant: margin).on()
|
||||
}
|
||||
|
||||
34
main/Extensions/Font.swift
Normal file
34
main/Extensions/Font.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
import UIKit
|
||||
|
||||
extension UIFont {
|
||||
func withTraits(traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
|
||||
UIFont(descriptor: fontDescriptor.withSymbolicTraits(traits)!, size: 0) // keep size as is
|
||||
}
|
||||
func bold() -> UIFont { withTraits(traits: .traitBold) }
|
||||
func italic() -> UIFont { withTraits(traits: .traitItalic) }
|
||||
func monoSpace() -> UIFont {
|
||||
let traits = fontDescriptor.object(forKey: .traits) as? [UIFontDescriptor.TraitKey: Any] ?? [:]
|
||||
let weight = (traits[.weight] as? CGFloat) ?? UIFont.Weight.regular.rawValue
|
||||
return .monospacedDigitSystemFont(ofSize: pointSize, weight: .init(rawValue: weight))
|
||||
}
|
||||
}
|
||||
|
||||
extension NSMutableAttributedString {
|
||||
static private var def: UIFont = .preferredFont(forTextStyle: .body)
|
||||
|
||||
func normal(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: .preferredFont(forTextStyle: style)) }
|
||||
func bold(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: UIFont.preferredFont(forTextStyle: style).bold()) }
|
||||
func italic(_ str: String, _ style: UIFont.TextStyle = .body) -> Self { append(str, withFont: UIFont.preferredFont(forTextStyle: style).italic()) }
|
||||
|
||||
func h1(_ str: String) -> Self { normal(str, .title1) }
|
||||
func h2(_ str: String) -> Self { normal(str, .title2) }
|
||||
func h3(_ str: String) -> Self { normal(str, .title3) }
|
||||
|
||||
private func append(_ str: String, withFont: UIFont) -> Self {
|
||||
append(NSAttributedString(string: str, attributes: [
|
||||
.font : withFont,
|
||||
.foregroundColor : UIColor.sysFg
|
||||
]))
|
||||
return self
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
private let dateTimeFormat = DateFormatter(withFormat: "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
extension DateFormatter {
|
||||
convenience init(withFormat: String) {
|
||||
self.init()
|
||||
@@ -9,26 +7,18 @@ extension DateFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
extension Timestamp {
|
||||
/// Time string with format `yyyy-MM-dd HH:mm:ss`
|
||||
func asDateTime() -> String {
|
||||
dateTimeFormat.string(from: Date.init(timeIntervalSince1970: Double(self)))
|
||||
}
|
||||
|
||||
extension Date {
|
||||
/// Convert `Timestamp` to `Date`
|
||||
func toDate() -> Date {
|
||||
Date(timeIntervalSince1970: Double(self))
|
||||
}
|
||||
|
||||
init(_ ts: Timestamp) { self.init(timeIntervalSince1970: Double(ts)) }
|
||||
/// Convert `Date` to `Timestamp`
|
||||
var timestamp: Timestamp { get { Timestamp(self.timeIntervalSince1970) } }
|
||||
}
|
||||
|
||||
extension Timestamp {
|
||||
/// Current time as `Timestamp` (second accuracy)
|
||||
static func now() -> Timestamp {
|
||||
Timestamp(Date().timeIntervalSince1970)
|
||||
}
|
||||
|
||||
static func now() -> Timestamp { Date().timestamp }
|
||||
/// Create `Timestamp` with `now() - minutes * 60`
|
||||
static func past(minutes: Int) -> Timestamp {
|
||||
now() - Timestamp(minutes * 60)
|
||||
}
|
||||
static func past(minutes: Int) -> Timestamp { now() - Timestamp(minutes * 60) }
|
||||
}
|
||||
|
||||
extension Timer {
|
||||
@@ -39,6 +29,24 @@ extension Timer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DateFormat
|
||||
|
||||
enum DateFormat {
|
||||
private static let _hms = DateFormatter(withFormat: "yyyy-MM-dd HH:mm:ss")
|
||||
private static let _hm = DateFormatter(withFormat: "yyyy-MM-dd HH:mm")
|
||||
|
||||
/// Format: `yyyy-MM-dd HH:mm:ss`
|
||||
static func seconds(_ date: Date) -> String { _hms.string(from: date) }
|
||||
/// Format: `yyyy-MM-dd HH:mm:ss`
|
||||
static func seconds(_ ts: Timestamp) -> String { _hms.string(from: Date(ts)) }
|
||||
/// Format: `yyyy-MM-dd HH:mm`
|
||||
static func minutes(_ date: Date) -> String { _hm.string(from: date) }
|
||||
/// Format: `yyyy-MM-dd HH:mm`
|
||||
static func minutes(_ ts: Timestamp) -> String { _hm.string(from: Date(ts)) }
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TimeFormat
|
||||
|
||||
struct TimeFormat {
|
||||
|
||||
@@ -69,7 +69,7 @@ class TVCPreviousRecords: UITableViewController, EditActionsRemove {
|
||||
let x = dataSource[indexPath.row]
|
||||
cell.textLabel?.text = x.title ?? x.fallbackTitle
|
||||
cell.textLabel?.textColor = (x.title == nil) ? .systemGray : nil
|
||||
cell.detailTextLabel?.text = "at \(x.start.asDateTime()), duration: \(x.durationString ?? "?")"
|
||||
cell.detailTextLabel?.text = "at \(DateFormat.seconds(x.start)), duration: \(x.durationString ?? "?")"
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
||||
inputTitle.text = record.title
|
||||
inputNotes.text = record.notes
|
||||
inputDetails.text = """
|
||||
Start: \(record.start.asDateTime())
|
||||
End: \(record.stop?.asDateTime() ?? "?")
|
||||
Start: \(DateFormat.seconds(record.start))
|
||||
End: \(record.stop == nil ? "?" : DateFormat.seconds(record.stop!))
|
||||
Duration: \(record.durationString ?? "?")
|
||||
"""
|
||||
validateSaveButton()
|
||||
|
||||
@@ -12,10 +12,7 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate {
|
||||
override func viewDidLoad() {
|
||||
prevRecController = (children.first as! UINavigationController)
|
||||
prevRecController.delegate = self
|
||||
// Duplicate font attributes but set monospace
|
||||
let traits = timeLabel.font.fontDescriptor.object(forKey: .traits) as? [UIFontDescriptor.TraitKey: Any] ?? [:]
|
||||
let weight = traits[.weight] as? CGFloat ?? UIFont.Weight.regular.rawValue
|
||||
timeLabel.font = UIFont.monospacedDigitSystemFont(ofSize: timeLabel.font.pointSize, weight: UIFont.Weight(rawValue: weight))
|
||||
timeLabel.font = timeLabel.font.monoSpace()
|
||||
// hide timer if not running
|
||||
updateUI(setRecording: false, animated: false)
|
||||
currentRecording = RecordingsDB.getCurrent()
|
||||
@@ -71,7 +68,7 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate {
|
||||
guard let r = currentRecording, r.stop == nil else {
|
||||
return
|
||||
}
|
||||
recordingTimer = Timer.repeating(0.086, call: #selector(timerCallback(_:)), on: self, userInfo: r.start.toDate())
|
||||
recordingTimer = Timer.repeating(0.086, call: #selector(timerCallback(_:)), on: self, userInfo: Date(r.start))
|
||||
updateUI(setRecording: true, animated: animate)
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class TVCHostDetails: UITableViewController {
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "HostDetailCell")!
|
||||
let src = dataSource[indexPath.row]
|
||||
cell.textLabel?.text = src.ts.asDateTime()
|
||||
cell.textLabel?.text = DateFormat.seconds(src.ts)
|
||||
cell.detailTextLabel?.text = (src.total > 1) ? "\(src.total)x" : nil
|
||||
cell.imageView?.image = (src.blocked > 0 ? UIImage(named: "shield-x") : nil)
|
||||
return cell
|
||||
|
||||
@@ -18,6 +18,8 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
|
||||
@IBOutlet private var rangeView: UIView!
|
||||
@IBOutlet private var buttonRangeStart: UIButton!
|
||||
@IBOutlet private var buttonRangeEnd: UIButton!
|
||||
private lazy var tsRangeA: Timestamp = AppDB?.dnsLogsMinDate() ?? 0
|
||||
private lazy var tsRangeB: Timestamp = .now()
|
||||
|
||||
// order by
|
||||
@IBOutlet private var orderbyType: UISegmentedControl!
|
||||
@@ -35,12 +37,11 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
|
||||
durationSlider.value = Float(durationTimes.firstIndex(of: Pref.DateFilter.LastXMin) ?? 0) / 9
|
||||
durationSliderChanged(durationSlider)
|
||||
|
||||
var a = Timestamp(4).asDateTime() // TODO: load from preferences
|
||||
var b = Timestamp.now().asDateTime()
|
||||
a.removeLast(3) // remove seconds
|
||||
b.removeLast(3)
|
||||
buttonRangeStart.setTitle(a, for: .normal)
|
||||
buttonRangeEnd.setTitle(b, for: .normal)
|
||||
// Force set seconds to 00 and 59 respectively. Its retained during change.
|
||||
tsRangeA = tsRangeA - tsRangeA % 60 + 00
|
||||
tsRangeB = tsRangeB - tsRangeB % 60 + 59
|
||||
buttonRangeStart.setTitle(DateFormat.minutes(tsRangeA), for: .normal)
|
||||
buttonRangeEnd.setTitle(DateFormat.minutes(tsRangeB), for: .normal)
|
||||
|
||||
orderbyType.selectedSegmentIndex = Pref.DateFilter.OrderBy.rawValue
|
||||
orderbyAsc.selectedSegmentIndex = (Pref.DateFilter.OrderAsc ? 0 : 1)
|
||||
@@ -65,7 +66,13 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
@IBAction private func didTapRangeButton(_ sender: UIButton) {
|
||||
// TODO: show date picker
|
||||
let flag = (sender == buttonRangeStart)
|
||||
DatePickerAlert(presentIn: self, configure: {
|
||||
$0.setDate(Date(flag ? self.tsRangeA : self.tsRangeB), animated: false)
|
||||
}, onSuccess: {
|
||||
flag ? (self.tsRangeA = $0.timestamp) : (self.tsRangeB = $0.timestamp)
|
||||
sender.setTitle(DateFormat.minutes($0), for: .normal)
|
||||
})
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
||||
@@ -85,6 +92,7 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
|
||||
case 2: orderType = .Count
|
||||
default: preconditionFailure()
|
||||
}
|
||||
sync.pause()
|
||||
let orderAsc = (orderbyAsc.selectedSegmentIndex == 0)
|
||||
if Pref.DateFilter.OrderBy != orderType || Pref.DateFilter.OrderAsc != orderAsc {
|
||||
Pref.DateFilter.OrderBy = orderType
|
||||
@@ -96,6 +104,7 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
|
||||
Pref.DateFilter.LastXMin = newXMin
|
||||
NotifyDateFilterChanged.post()
|
||||
}
|
||||
sync.continue()
|
||||
dismiss(animated: true)
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user