Show "no results" in recordings + mark recording as shared

This commit is contained in:
relikd
2020-08-28 22:05:49 +02:00
parent 42aa7cf926
commit 448d69c6d8
8 changed files with 103 additions and 27 deletions

View File

@@ -33,3 +33,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// This is a known issue and tolerated.
}
}
extension URL {
@discardableResult func open() -> Bool { UIApplication.shared.openURL(self) }
}

View File

@@ -22,9 +22,12 @@ extension SQLiteDatabase {
}
if version != 2 {
// version 0 -> 1: req(domain) -> heap(fqdn, domain)
// version 1 -> 2: rec(+subtitle)
// version 1 -> 2: rec(+subtitle, +opt)
if version == 1 {
try run(sql: "ALTER TABLE rec ADD COLUMN subtitle TEXT;")
transaction("""
ALTER TABLE rec ADD COLUMN subtitle TEXT;
ALTER TABLE rec ADD COLUMN opt INTEGER;
""")
}
try run(sql: "PRAGMA user_version = 2;")
}
@@ -294,11 +297,13 @@ extension CreateTable {
appid TEXT,
title TEXT,
subtitle TEXT,
notes TEXT
notes TEXT,
opt INTEGER
);
"""}
}
let readRecordingSelect = "id, start, stop, appid, title, subtitle, notes, opt"
struct Recording {
let id: sqlite3_int64
let start: Timestamp
@@ -307,6 +312,7 @@ struct Recording {
var title: String? = nil
var subtitle: String? = nil
var notes: String? = nil
var shared: Bool = false
}
typealias AppBundleInfo = (bundleId: String, name: String?, author: String?)
@@ -335,8 +341,9 @@ extension SQLiteDatabase {
/// Update given recording by replacing `title`, `appid`, and `notes` with new values.
func recordingUpdate(_ r: Recording) {
try? run(sql: "UPDATE rec SET appid = ?, title = ?, subtitle = ?, notes = ? WHERE id = ? LIMIT 1;",
bind: [BindTextOrNil(r.appId), BindTextOrNil(r.title), BindTextOrNil(r.subtitle), BindTextOrNil(r.notes), BindInt64(r.id)]) { stmt -> Void in
try? run(sql: "UPDATE rec SET appid = ?, title = ?, subtitle = ?, notes = ?, opt = ? WHERE id = ? LIMIT 1;",
bind: [BindTextOrNil(r.appId), BindTextOrNil(r.title), BindTextOrNil(r.subtitle),
BindTextOrNil(r.notes), r.shared ? BindInt32(1) : BindNull(), BindInt64(r.id)]) { stmt -> Void in
sqlite3_step(stmt)
}
}
@@ -355,18 +362,20 @@ extension SQLiteDatabase {
private func readRecording(_ stmt: OpaquePointer) -> Recording {
let end = col_ts(stmt, 2)
let opt = sqlite3_column_int(stmt, 7)
return Recording(id: sqlite3_column_int64(stmt, 0),
start: col_ts(stmt, 1),
stop: end == 0 ? nil : end,
appId: col_text(stmt, 3),
title: col_text(stmt, 4),
subtitle: col_text(stmt, 5),
notes: col_text(stmt, 6))
notes: col_text(stmt, 6),
shared: opt > 0)
}
/// `WHERE stop IS NULL`
func recordingGetOngoing() -> Recording? {
try? run(sql: "SELECT id, start, stop, appid, title, subtitle, notes FROM rec WHERE stop IS NULL LIMIT 1;") {
try? run(sql: "SELECT \(readRecordingSelect) FROM rec WHERE stop IS NULL LIMIT 1;") {
try ifStep($0, SQLITE_ROW)
return readRecording($0)
}
@@ -382,14 +391,14 @@ extension SQLiteDatabase {
/// `WHERE stop IS NOT NULL`
func recordingGetAll() -> [Recording]? {
try? run(sql: "SELECT id, start, stop, appid, title, subtitle, notes FROM rec WHERE stop IS NOT NULL;") {
try? run(sql: "SELECT \(readRecordingSelect) FROM rec WHERE stop IS NOT NULL;") {
allRows($0) { readRecording($0) }
}
}
/// `WHERE id = ?`
private func recordingGet(withID: sqlite3_int64) throws -> Recording {
try run(sql: "SELECT id, start, stop, appid, title, subtitle, notes FROM rec WHERE id = ? LIMIT 1;", bind: [BindInt64(withID)]) {
try run(sql: "SELECT \(readRecordingSelect) FROM rec WHERE id = ? LIMIT 1;", bind: [BindInt64(withID)]) {
try ifStep($0, SQLITE_ROW)
return readRecording($0)
}

View File

@@ -169,6 +169,10 @@ protocol DBBinding {
func bind(_ stmt: OpaquePointer!, _ col: Int32) -> Int32
}
struct BindNull : DBBinding {
func bind(_ stmt: OpaquePointer!, _ col: Int32) -> Int32 { sqlite3_bind_null(stmt, col) }
}
struct BindInt32 : DBBinding {
let raw: Int32
init(_ value: Int32) { raw = value }

View File

@@ -31,8 +31,8 @@ func ErrorAlert(_ errorDescription: String, buttonText: String = "Dismiss") -> U
/// - 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")
func AskAlert(title: String?, text: String?, buttonText: String = "Continue", cancelButton: String = "Cancel", buttonStyle: UIAlertAction.Style = .default, action: @escaping (UIAlertController) -> Void) -> UIAlertController {
let alert = Alert(title: title, text: text, buttonText: cancelButton)
alert.addAction(UIAlertAction(title: buttonText, style: buttonStyle) { _ in action(alert) })
return alert
}
@@ -42,9 +42,7 @@ func NotificationsDisabledAlert(presentIn viewController: UIViewController) {
AskAlert(title: "Notifications Disabled",
text: "Go to System Settings > Notifications > AppCheck to re-enable notifications.",
buttonText: "Open settings") { _ in
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.openURL(url)
}
URL(string: UIApplication.openSettingsURLString)?.open()
}.presentIn(viewController)
}

View File

@@ -25,6 +25,13 @@ extension String {
let parts = components(separatedBy: ".")
return parts.count == 2 && listOfSLDs[parts.last!]?[parts.first!] ?? false
}
func isValidBundleId() -> Bool {
let regex = try! NSRegularExpression(pattern: #"^[A-Za-z0-9\.\-]{1,155}$"#, options: .anchorsMatchLines)
let range = NSRange(location: 0, length: self.utf16.count)
let matches = regex.matches(in: self, options: .anchored, range: range)
return matches.count == 1
}
}
private var listOfSLDs: [String : [String : Bool]] = {

View File

@@ -212,6 +212,23 @@
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RecordNoResultsCell" textLabel="bmQ-Cn-BOm" style="IBUITableViewCellStyleDefault" id="JZ4-vZ-MnG">
<rect key="frame" x="0.0" y="172.5" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JZ4-vZ-MnG" id="TWb-p9-EMM">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text=" no results " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.80000001192092896" adjustsFontSizeToFit="NO" id="bmQ-Cn-BOm">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="50g-BI-Q6S" id="SFM-IM-FRx"/>
@@ -222,7 +239,7 @@
<rightBarButtonItems>
<barButtonItem systemItem="action" id="UkE-Wi-JjW">
<connections>
<segue destination="P0a-ZP-uEV" kind="modal" id="s3J-zL-4zK"/>
<segue destination="P0a-ZP-uEV" kind="modal" identifier="openContributeSegue" id="s3J-zL-4zK"/>
</connections>
</barButtonItem>
<barButtonItem image="line-expand" id="xLc-O7-KVB">
@@ -274,8 +291,8 @@
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eWC-xB-CJe">
<rect key="frame" x="292" y="12" width="20" height="20"/>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="eWC-xB-CJe">
<rect key="frame" x="275" y="3.5" width="37" height="37"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
@@ -294,6 +311,7 @@
</view>
<connections>
<outlet property="sendActivity" destination="eWC-xB-CJe" id="rx3-Jz-ppT"/>
<outlet property="sendButton" destination="PWY-06-ykI" id="eEf-hW-VIs"/>
<outlet property="text" destination="fFm-v5-DGy" id="fwm-RE-gHu"/>
</connections>
</viewController>

View File

@@ -2,6 +2,7 @@ import UIKit
class TVCRecordingDetails: UITableViewController, EditActionsRemove {
var record: Recording!
var noResults: Bool = false
private lazy var isLongRecording: Bool = record.isLongTerm
@IBOutlet private var shareButton: UIBarButtonItem!
@@ -10,7 +11,8 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
/// Sorted by `ts` in ascending order (oldest first)
private lazy var dataSourceRaw: [DomainTsPair] = {
let list = RecordingsDB.details(record)
shareButton.isEnabled = list.count > 0
noResults = list.count == 0
shareButton.isEnabled = !noResults
return list
}()
/// Sorted by `count` (descending), then alphabetically
@@ -26,6 +28,14 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
override func viewDidLoad() {
title = record.title ?? record.fallbackTitle
NotifyRecordingChanged.observe(call: #selector(recordingDidChange(_:)), on: self)
}
@objc private func recordingDidChange(_ notification: Notification) {
let (rec, deleted) = notification.object as! (Recording, Bool)
if rec.id == record.id, !deleted {
record = rec // almost exclusively when 'shared' is set true
}
}
@IBAction private func toggleDisplayStyle(_ sender: UIBarButtonItem) {
@@ -34,6 +44,20 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
tableView.reloadData()
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "openContributeSegue" && record.shared {
let alert = Alert(title: nil, text: "You have shared this recording already.")
if let bid = record.appId, bid.isValidBundleId() {
alert.addAction(UIAlertAction.init(title: "Open results", style: .default, handler: { _ in
URL(string: "http://127.0.0.1/redirect.html?id=\(bid)")?.open()
}))
}
alert.presentIn(self)
return false
}
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let tgt = segue.destination as? VCShareRecording {
tgt.record = self.record
@@ -44,12 +68,15 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
// MARK: - Table View Data Source
override func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
showRaw ? dataSourceRaw.count : dataSourceSum.count
max(1, showRaw ? dataSourceRaw.count : dataSourceSum.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if showRaw {
if noResults {
cell = tableView.dequeueReusableCell(withIdentifier: "RecordNoResultsCell")!
cell.textLabel?.text = " empty recording "
} else if showRaw {
let x = dataSourceRaw[indexPath.row]
if isLongRecording {
cell = tableView.dequeueReusableCell(withIdentifier: "RecordDetailLongCell")!
@@ -73,11 +100,11 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
// MARK: - Editing
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
getRowActionsIOS9(indexPath, tableView)
noResults ? nil : getRowActionsIOS9(indexPath, tableView)
}
@available(iOS 11.0, *)
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
getRowActionsIOS11(indexPath)
noResults ? nil : getRowActionsIOS11(indexPath)
}
func editableRowCallback(_ index: IndexPath, _ action: RowAction, _ userInfo: Any?) -> Bool {
@@ -101,7 +128,8 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
tableView.deleteRows(at: [index], with: .automatic)
}
}
shareButton.isEnabled = dataSourceRaw.count > 0
noResults = dataSourceRaw.count == 0
shareButton.isEnabled = !noResults
return true
}
}

View File

@@ -6,11 +6,14 @@ class VCShareRecording : UIViewController {
private var jsonData: Data?
@IBOutlet private var text : UITextView!
@IBOutlet private var sendButton: UIBarButtonItem!
@IBOutlet private var sendActivity : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
sendButton.isEnabled = !record.shared
let start = record.start
let comp = Calendar.current.dateComponents([.weekOfYear, .yearForWeekOfYear], from: Date(start))
let wkYear = "\(comp.yearForWeekOfYear ?? 0).\(comp.weekOfYear ?? 0)"
@@ -68,6 +71,7 @@ class VCShareRecording : UIViewController {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
var rec = record!
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async { [weak self] in
@@ -87,6 +91,12 @@ class VCShareRecording : UIViewController {
self?.banner(.fail, "Server couldn't parse request.\nTry again later.")
return
}
// update db, mark record as shared
sender.isEnabled = false
rec.shared = true // in case view was closed
self?.record = rec // in case view is still open
RecordingsDB.update(rec) // rec cause self may not be available
// notify user about results
var autoHide = true
if v == 1, let urlStr = json?["url"] as? String {
let nextUpdateIn = json?["when"] as? Int
@@ -116,12 +126,10 @@ class VCShareRecording : UIViewController {
msg += "shortly. "
}
msg += "Open results webpage now?"
let alert = Alert(title: "Thank you", text: msg, buttonText: "Not now")
alert.addAction(UIAlertAction(title: "Show results", style: .default) { _ in
AskAlert(title: "Thank you", text: msg, buttonText: "Show results", cancelButton: "Not now") { _ in
if let url = URL(string: urlStr) {
UIApplication.shared.openURL(url)
}
})
alert.presentIn(self)
}.presentIn(self)
}
}