Recording details duplicate and display
This commit is contained in:
@@ -14,7 +14,7 @@ public class SOCKS5Adapter: AdapterSocket {
|
||||
|
||||
var internalStatus: SOCKS5AdapterStatus = .invalid
|
||||
|
||||
let helloData = Data(bytes: UnsafePointer<UInt8>(([0x05, 0x01, 0x00] as [UInt8])), count: 3)
|
||||
let helloData = Data([0x05, 0x01, 0x00])
|
||||
|
||||
public enum ReadTag: Int {
|
||||
case methodResponse = -20000, connectResponseFirstPart, connectResponseSecondPart
|
||||
|
||||
@@ -48,6 +48,7 @@ class DBWrapper {
|
||||
// MARK: - Init
|
||||
|
||||
func initContentOfDB() {
|
||||
QLog.Debug("SQLite path: \(URL.internalDB())")
|
||||
DispatchQueue.global().async {
|
||||
#if IOS_SIMULATOR
|
||||
self.generateTestData()
|
||||
@@ -226,7 +227,9 @@ class DBWrapper {
|
||||
func listOfRecordings() -> [Recording] { AppDB?.allRecordings() ?? [] }
|
||||
func recordingGetCurrent() -> Recording? { AppDB?.ongoingRecording() }
|
||||
func recordingStartNew() -> Recording? { try? AppDB?.startNewRecording() }
|
||||
func recordingStopAll() { AppDB?.stopRecordings() }
|
||||
|
||||
func recordingStop(_ r: inout Recording) { AppDB?.stopRecording(&r) }
|
||||
func recordingPersist(_ r: Recording) { AppDB?.persistRecordingLogs(r) }
|
||||
|
||||
func recordingUpdate(_ r: Recording) {
|
||||
AppDB?.updateRecording(r)
|
||||
@@ -239,6 +242,10 @@ class DBWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
func recordingDetails(_ r: Recording) -> [(domain: String?, count: Int32)]? {
|
||||
AppDB?.getRecordingsLogs(r)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Helper methods
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ struct GroupedDomain {
|
||||
|
||||
struct FilterOptions: OptionSet {
|
||||
let rawValue: Int32
|
||||
static let none = FilterOptions(rawValue: 0)
|
||||
static let none = FilterOptions([])
|
||||
static let blocked = FilterOptions(rawValue: 1 << 0)
|
||||
static let ignored = FilterOptions(rawValue: 1 << 1)
|
||||
static let any = FilterOptions(rawValue: 0b11)
|
||||
@@ -28,7 +28,6 @@ enum SQLiteError: Error {
|
||||
class SQLiteDatabase {
|
||||
private let dbPointer: OpaquePointer?
|
||||
private init(dbPointer: OpaquePointer?) {
|
||||
// print("SQLite path: \(URL.internalDB())")
|
||||
self.dbPointer = dbPointer
|
||||
}
|
||||
|
||||
@@ -158,6 +157,7 @@ extension SQLiteDatabase {
|
||||
try? self.createTable(table: DNSQueryT.self)
|
||||
try? self.createTable(table: DNSFilterT.self)
|
||||
try? self.createTable(table: Recording.self)
|
||||
try? self.createTable(table: RecordingLog.self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,9 +172,9 @@ private struct DNSQueryT: SQLTable {
|
||||
static var createStatement: String {
|
||||
return """
|
||||
CREATE TABLE IF NOT EXISTS req(
|
||||
ts BIGINT DEFAULT (strftime('%s','now')),
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
logOpt INT DEFAULT 0
|
||||
ts INTEGER DEFAULT (strftime('%s','now')),
|
||||
domain TEXT NOT NULL,
|
||||
logOpt INTEGER DEFAULT 0
|
||||
);
|
||||
"""
|
||||
}
|
||||
@@ -254,8 +254,8 @@ private struct DNSFilterT: SQLTable {
|
||||
static var createStatement: String {
|
||||
return """
|
||||
CREATE TABLE IF NOT EXISTS filter(
|
||||
domain VARCHAR(255) UNIQUE NOT NULL,
|
||||
opt INT DEFAULT 0
|
||||
domain TEXT UNIQUE NOT NULL,
|
||||
opt INTEGER DEFAULT 0
|
||||
);
|
||||
"""
|
||||
}
|
||||
@@ -310,6 +310,7 @@ extension SQLiteDatabase {
|
||||
// MARK: - Recordings
|
||||
|
||||
struct Recording: SQLTable {
|
||||
let id: sqlite3_int64
|
||||
let start: Timestamp
|
||||
let stop: Timestamp?
|
||||
var appId: String? = nil
|
||||
@@ -318,10 +319,11 @@ struct Recording: SQLTable {
|
||||
static var createStatement: String {
|
||||
return """
|
||||
CREATE TABLE IF NOT EXISTS rec(
|
||||
start BIGINT DEFAULT (strftime('%s','now')),
|
||||
stop BIGINT,
|
||||
appid VARCHAR(255),
|
||||
title VARCHAR(255),
|
||||
id INTEGER PRIMARY KEY,
|
||||
start INTEGER DEFAULT (strftime('%s','now')),
|
||||
stop INTEGER,
|
||||
appid TEXT,
|
||||
title TEXT,
|
||||
notes TEXT
|
||||
);
|
||||
"""
|
||||
@@ -332,33 +334,37 @@ extension SQLiteDatabase {
|
||||
|
||||
// MARK: write
|
||||
|
||||
func startNewRecording(_ title: String? = nil, appBundle: String? = nil) throws -> Recording {
|
||||
try run(sql: "INSERT INTO rec (title, appid) VALUES (?, ?);", bind: {
|
||||
self.bindTextOrNil($0, 1, title) && self.bindTextOrNil($0, 2, appBundle)
|
||||
}) { stmt -> Recording in
|
||||
func startNewRecording() throws -> Recording {
|
||||
try run(sql: "INSERT INTO rec (stop) VALUES (NULL);", bind: nil) { stmt -> Recording in
|
||||
try ifStep(stmt, SQLITE_DONE)
|
||||
return ongoingRecording()!
|
||||
return try getRecording(withID: sqlite3_last_insert_rowid(dbPointer))
|
||||
}
|
||||
}
|
||||
|
||||
func stopRecordings() {
|
||||
try? run(sql: "UPDATE rec SET stop = (strftime('%s','now')) WHERE stop IS NULL;", bind: nil) { stmt -> Void in
|
||||
sqlite3_step(stmt)
|
||||
func stopRecording(_ r: inout Recording) {
|
||||
guard r.stop == nil else { return }
|
||||
let theID = r.id
|
||||
try? run(sql: "UPDATE rec SET stop = (strftime('%s','now')) WHERE id = ? LIMIT 1;", bind: {
|
||||
self.bindInt64($0, 1, theID)
|
||||
}) { stmt -> Void in
|
||||
try ifStep(stmt, SQLITE_DONE)
|
||||
r = try getRecording(withID: theID)
|
||||
}
|
||||
}
|
||||
|
||||
func updateRecording(_ r: Recording) {
|
||||
try? run(sql: "UPDATE rec SET title = ?, appid = ?, notes = ? WHERE start = ? LIMIT 1;", bind: {
|
||||
try? run(sql: "UPDATE rec SET title = ?, appid = ?, notes = ? WHERE id = ? LIMIT 1;", bind: {
|
||||
self.bindTextOrNil($0, 1, r.title) && self.bindTextOrNil($0, 2, r.appId)
|
||||
&& self.bindTextOrNil($0, 3, r.notes) && self.bindInt64($0, 4, r.start)
|
||||
&& self.bindTextOrNil($0, 3, r.notes) && self.bindInt64($0, 4, r.id)
|
||||
}) { stmt -> Void in
|
||||
sqlite3_step(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteRecording(_ r: Recording) throws -> Bool {
|
||||
try run(sql: "DELETE FROM rec WHERE start = ? LIMIT 1;", bind: {
|
||||
self.bindInt64($0, 1, r.start)
|
||||
_ = try? deleteRecordingLogs(r.id)
|
||||
return try run(sql: "DELETE FROM rec WHERE id = ? LIMIT 1;", bind: {
|
||||
self.bindInt64($0, 1, r.id)
|
||||
}) {
|
||||
try ifStep($0, SQLITE_DONE)
|
||||
return sqlite3_changes(dbPointer) > 0
|
||||
@@ -368,12 +374,13 @@ extension SQLiteDatabase {
|
||||
// MARK: read
|
||||
|
||||
func readRecording(_ stmt: OpaquePointer) -> Recording {
|
||||
let end = sqlite3_column_int64(stmt, 1)
|
||||
return Recording(start: sqlite3_column_int64(stmt, 0),
|
||||
let end = sqlite3_column_int64(stmt, 2)
|
||||
return Recording(id: sqlite3_column_int64(stmt, 0),
|
||||
start: sqlite3_column_int64(stmt, 1),
|
||||
stop: end == 0 ? nil : end,
|
||||
appId: readText(stmt, 2),
|
||||
title: readText(stmt, 3),
|
||||
notes: readText(stmt, 4))
|
||||
appId: readText(stmt, 3),
|
||||
title: readText(stmt, 4),
|
||||
notes: readText(stmt, 5))
|
||||
}
|
||||
|
||||
func ongoingRecording() -> Recording? {
|
||||
@@ -388,4 +395,68 @@ extension SQLiteDatabase {
|
||||
allRows($0) { readRecording($0) }
|
||||
}
|
||||
}
|
||||
|
||||
func getRecording(withID: sqlite3_int64) throws -> Recording {
|
||||
try run(sql: "SELECT * FROM rec WHERE id = ? LIMIT 1;", bind: {
|
||||
self.bindInt64($0, 1, withID)
|
||||
}) {
|
||||
try ifStep($0, SQLITE_ROW)
|
||||
return readRecording($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
private struct RecordingLog: SQLTable {
|
||||
let rID: Int32
|
||||
let ts: Timestamp
|
||||
let domain: String
|
||||
static var createStatement: String {
|
||||
return """
|
||||
CREATE TABLE IF NOT EXISTS recLog(
|
||||
rid INTEGER REFERENCES rec(id) ON DELETE CASCADE,
|
||||
ts INTEGER,
|
||||
domain TEXT
|
||||
);
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
extension SQLiteDatabase {
|
||||
|
||||
// MARK: write
|
||||
|
||||
func persistRecordingLogs(_ r: Recording) {
|
||||
guard let end = r.stop else {
|
||||
return
|
||||
}
|
||||
try? run(sql: """
|
||||
INSERT INTO recLog (rid, ts, domain) SELECT ?, ts, domain FROM req
|
||||
WHERE req.ts >= ? AND req.ts <= ?
|
||||
""", bind: {
|
||||
self.bindInt64($0, 1, r.id) && self.bindInt64($0, 2, r.start) && self.bindInt64($0, 3, end)
|
||||
}) {
|
||||
try ifStep($0, SQLITE_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteRecordingLogs(_ recId: sqlite3_int64) throws -> Int32 {
|
||||
try run(sql: "DELETE FROM recLog WHERE rid = ?;", bind: {
|
||||
self.bindInt64($0, 1, recId)
|
||||
}) {
|
||||
try ifStep($0, SQLITE_DONE)
|
||||
return sqlite3_changes(dbPointer)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: read
|
||||
|
||||
func getRecordingsLogs(_ r: Recording) -> [(domain: String?, count: Int32)]? {
|
||||
try? run(sql: "SELECT domain, COUNT() FROM recLog WHERE rid = ? GROUP BY domain;", bind: {
|
||||
self.bindInt64($0, 1, r.id)
|
||||
}) {
|
||||
allRows($0) { (readText($0, 0), sqlite3_column_int($0, 1)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,7 @@ extension Array where Element == GroupedDomain {
|
||||
}
|
||||
|
||||
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 fallbackTitle: String { get { "Unnamed Recording #\(id)" } }
|
||||
var duration: Timestamp? { get { stop == nil ? nil : stop! - start } }
|
||||
var durationString: String? { get { stop == nil ? nil : TimeFormat.from(duration!) } }
|
||||
}
|
||||
|
||||
@@ -8,17 +8,14 @@ class TVCPreviousRecords: UITableViewController {
|
||||
NotifyRecordingChanged.observe(call: #selector(recordingDidChange(_:)), on: self)
|
||||
}
|
||||
|
||||
func stopRecording(_ record: Recording?) {
|
||||
guard let r = record?.stoppedCopy() else {
|
||||
return
|
||||
}
|
||||
func insertAndEditRecording(_ r: Recording) {
|
||||
insertNewRecord(r)
|
||||
editRecord(r, isNewRecording: true)
|
||||
}
|
||||
|
||||
@objc private func recordingDidChange(_ notification: Notification) {
|
||||
let (new, deleted) = notification.object as! (Recording, Bool)
|
||||
if let i = dataSource.firstIndex(where: { $0.start == new.start }) {
|
||||
if let i = dataSource.firstIndex(where: { $0.id == new.id }) {
|
||||
if deleted {
|
||||
dataSource.remove(at: i)
|
||||
tableView.deleteRows(at: [IndexPath(row: i)], with: .automatic)
|
||||
|
||||
@@ -2,14 +2,11 @@ import UIKit
|
||||
|
||||
class TVCRecordingDetails: UITableViewController {
|
||||
var record: Recording!
|
||||
private var dataSource: [(domain: String, count: Int)] = [
|
||||
("apple.com", 3),
|
||||
("cdn.apple.com", 1)
|
||||
]
|
||||
private var dataSource: [(domain: String?, count: Int32)]!
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = record.title ?? record.fallbackTitle
|
||||
// TODO: load db entries
|
||||
dataSource = DBWrp.recordingDetails(record) ?? []
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,24 +35,24 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
||||
if deleteOnCancel { // aka newly created
|
||||
// if remains true, `viewDidDisappear` will delete the record
|
||||
deleteOnCancel = false
|
||||
// TODO: copy db entries in new table for editing
|
||||
}
|
||||
QLog.Debug("updating record \(record.start)")
|
||||
QLog.Debug("updating record #\(record.id)")
|
||||
record.title = (inputTitle.text == "") ? nil : inputTitle.text
|
||||
record.notes = (inputNotes.text == "") ? nil : inputNotes.text
|
||||
dismiss(animated: true) {
|
||||
DBWrp.recordingUpdate(self.record)
|
||||
DBWrp.recordingPersist(self.record)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func didTapCancel(_ sender: UIBarButtonItem) {
|
||||
QLog.Debug("discard edit of record \(record.start)")
|
||||
QLog.Debug("discard edit of record #\(record.id)")
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
if deleteOnCancel {
|
||||
QLog.Debug("deleting record \(record.start)")
|
||||
QLog.Debug("deleting record #\(record.id)")
|
||||
DBWrp.recordingDelete(record)
|
||||
deleteOnCancel = false
|
||||
}
|
||||
|
||||
@@ -45,9 +45,10 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate {
|
||||
startTimer(animate: true)
|
||||
} else {
|
||||
stopTimer(animate: true)
|
||||
DBWrp.recordingStopAll()
|
||||
DBWrp.recordingStop(¤tRecording!)
|
||||
prevRecController.popToRootViewController(animated: true)
|
||||
(prevRecController.topViewController as! TVCPreviousRecords).stopRecording(currentRecording!)
|
||||
let editVC = (prevRecController.topViewController as! TVCPreviousRecords)
|
||||
editVC.insertAndEditRecording(currentRecording!)
|
||||
currentRecording = nil // otherwise it will restart
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user