Fix update of 'last modified' cell if removing latest entries
This commit is contained in:
@@ -104,7 +104,7 @@ extension GroupedDomainDataSource {
|
|||||||
pipeline.reset(dataSource: logs)
|
pipeline.reset(dataSource: logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncUpdate(_: SyncUpdate, insert rows: SQLiteRowRange) {
|
func syncUpdate(_: SyncUpdate, insert rows: SQLiteRowRange, affects: SyncUpdateEnd) {
|
||||||
guard let latest = AppDB?.dnsLogsGrouped(range: rows, parentDomain: parent) else {
|
guard let latest = AppDB?.dnsLogsGrouped(range: rows, parentDomain: parent) else {
|
||||||
assertionFailure("NotifySyncInsert fired with empty range")
|
assertionFailure("NotifySyncInsert fired with empty range")
|
||||||
return
|
return
|
||||||
@@ -122,7 +122,12 @@ extension GroupedDomainDataSource {
|
|||||||
cellAnimationsCommit()
|
cellAnimationsCommit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncUpdate(_: SyncUpdate, remove rows: SQLiteRowRange) {
|
func syncUpdate(_ sender: SyncUpdate, remove rows: SQLiteRowRange, affects: SyncUpdateEnd) {
|
||||||
|
if affects == .Latest {
|
||||||
|
// TODO: alternatively query last modified from db (last entry _before_ range)
|
||||||
|
syncUpdate(sender, reset: sender.rows)
|
||||||
|
return
|
||||||
|
}
|
||||||
guard let outdated = AppDB?.dnsLogsGrouped(range: rows, parentDomain: parent),
|
guard let outdated = AppDB?.dnsLogsGrouped(range: rows, parentDomain: parent),
|
||||||
outdated.count > 0 else {
|
outdated.count > 0 else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -102,10 +102,10 @@ class SyncUpdate {
|
|||||||
if filterType == .ABRange {
|
if filterType == .ABRange {
|
||||||
// ... even if we filter a few later
|
// ... even if we filter a few later
|
||||||
if let r = rows(tsMin, tsMax, scope: newest) {
|
if let r = rows(tsMin, tsMax, scope: newest) {
|
||||||
notify(insert: r, front: false)
|
notify(insert: r, .Latest)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notify(insert: newest, front: false)
|
notify(insert: newest, .Latest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if filterType == .LastXMin {
|
if filterType == .LastXMin {
|
||||||
@@ -117,6 +117,10 @@ class SyncUpdate {
|
|||||||
|
|
||||||
// MARK: - Internal
|
// MARK: - Internal
|
||||||
|
|
||||||
|
private func rows(_ ts1: Timestamp, _ ts2: Timestamp, scope: SQLiteRowRange = (0,0)) -> SQLiteRowRange? {
|
||||||
|
AppDB?.dnsLogsRowRange(between: ts1, and: ts2, within: scope)
|
||||||
|
}
|
||||||
|
|
||||||
private func reloadRangeFromDB() {
|
private func reloadRangeFromDB() {
|
||||||
// `nil` is not SQLiteRowRange(0,0) aka. full collection.
|
// `nil` is not SQLiteRowRange(0,0) aka. full collection.
|
||||||
// `nil` means invalid range. e.g. ts restriction too high or empty db.
|
// `nil` means invalid range. e.g. ts restriction too high or empty db.
|
||||||
@@ -132,11 +136,11 @@ class SyncUpdate {
|
|||||||
if let (old, new) = tsEarliest <-/ newEarliest {
|
if let (old, new) = tsEarliest <-/ newEarliest {
|
||||||
if old != nil, (new == nil || new! < old!) {
|
if old != nil, (new == nil || new! < old!) {
|
||||||
if let r = rows(from(new), to(old!), scope: (0, range?.start ?? 0)) {
|
if let r = rows(from(new), to(old!), scope: (0, range?.start ?? 0)) {
|
||||||
notify(insert: r, front: true)
|
notify(insert: r, .Earliest)
|
||||||
}
|
}
|
||||||
} else if range != nil {
|
} else if range != nil {
|
||||||
if let r = rows(from(old), to(new!), scope: range!) {
|
if let r = rows(from(old), to(new!), scope: range!) {
|
||||||
notify(remove: r, front: true)
|
notify(remove: r, .Earliest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,34 +156,38 @@ class SyncUpdate {
|
|||||||
if let (old, new) = tsLatest <-/ newLatest {
|
if let (old, new) = tsLatest <-/ newLatest {
|
||||||
if old != nil, (new == nil || old! < new!) {
|
if old != nil, (new == nil || old! < new!) {
|
||||||
if let r = rows(from(old!), to(new), scope: (range?.end ?? 0, 0)) {
|
if let r = rows(from(old!), to(new), scope: (range?.end ?? 0, 0)) {
|
||||||
notify(insert: r, front: false)
|
notify(insert: r, .Latest)
|
||||||
}
|
}
|
||||||
} else if range != nil {
|
} else if range != nil {
|
||||||
// FIXME: removing latest entries will invalidate "last changed" label
|
// FIXME: removing latest entries will invalidate "last changed" label
|
||||||
if let r = rows(from(new!), to(old), scope: range!) {
|
if let r = rows(from(new!), to(old), scope: range!) {
|
||||||
notify(remove: r, front: false)
|
notify(remove: r, .Latest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func rows(_ ts1: Timestamp, _ ts2: Timestamp, scope: SQLiteRowRange = (0,0)) -> SQLiteRowRange? {
|
|
||||||
AppDB?.dnsLogsRowRange(between: ts1, and: ts2, within: scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// - Warning: Always call from a background thread!
|
/// - Warning: Always call from a background thread!
|
||||||
private func notify(insert r: SQLiteRowRange, front: Bool) {
|
private func notify(insert r: SQLiteRowRange, _ end: SyncUpdateEnd) {
|
||||||
if range == nil { range = r }
|
if range == nil { range = r }
|
||||||
else { front ? (range!.start = r.start) : (range!.end = r.end) }
|
else {
|
||||||
notifyObservers { $0.syncUpdate(self, insert: r) }
|
switch end {
|
||||||
|
case .Earliest: range!.start = r.start
|
||||||
|
case .Latest: range!.end = r.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyObservers { $0.syncUpdate(self, insert: r, affects: end) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// - Warning: `range` must not be `nil`!
|
/// - Warning: `range` must not be `nil`!
|
||||||
/// - Warning: Always call from a background thread!
|
/// - Warning: Always call from a background thread!
|
||||||
private func notify(remove r: SQLiteRowRange, front: Bool) {
|
private func notify(remove r: SQLiteRowRange, _ end: SyncUpdateEnd) {
|
||||||
front ? (range!.start = r.end + 1) : (range!.end = r.start - 1)
|
switch end {
|
||||||
|
case .Earliest: range!.start = r.end + 1
|
||||||
|
case .Latest: range!.end = r.start - 1
|
||||||
|
}
|
||||||
if range!.start > range!.end { range = nil }
|
if range!.start > range!.end { range = nil }
|
||||||
notifyObservers { $0.syncUpdate(self, remove: r) }
|
notifyObservers { $0.syncUpdate(self, remove: r, affects: end) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -211,6 +219,8 @@ private struct WeakObserver {
|
|||||||
weak var pullToRefresh: UIRefreshControl?
|
weak var pullToRefresh: UIRefreshControl?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SyncUpdateEnd { case Earliest, Latest }
|
||||||
|
|
||||||
protocol SyncUpdateDelegate : AnyObject {
|
protocol SyncUpdateDelegate : AnyObject {
|
||||||
/// `SyncUpdate` has unpredictable changes. Reload your `dataSource`.
|
/// `SyncUpdate` has unpredictable changes. Reload your `dataSource`.
|
||||||
/// - Warning: This function will **always** be called from a background thread.
|
/// - Warning: This function will **always** be called from a background thread.
|
||||||
@@ -218,11 +228,11 @@ protocol SyncUpdateDelegate : AnyObject {
|
|||||||
|
|
||||||
/// `SyncUpdate` added new `rows` to database. Sync changes to your `dataSource`.
|
/// `SyncUpdate` added new `rows` to database. Sync changes to your `dataSource`.
|
||||||
/// - Warning: This function will **always** be called from a background thread.
|
/// - Warning: This function will **always** be called from a background thread.
|
||||||
func syncUpdate(_ sender: SyncUpdate, insert rows: SQLiteRowRange)
|
func syncUpdate(_ sender: SyncUpdate, insert rows: SQLiteRowRange, affects: SyncUpdateEnd)
|
||||||
|
|
||||||
/// `SyncUpdate` outdated some `rows` in database. Sync changes to your `dataSource`.
|
/// `SyncUpdate` outdated some `rows` in database. Sync changes to your `dataSource`.
|
||||||
/// - Warning: This function will **always** be called from a background thread.
|
/// - Warning: This function will **always** be called from a background thread.
|
||||||
func syncUpdate(_ sender: SyncUpdate, remove rows: SQLiteRowRange)
|
func syncUpdate(_ sender: SyncUpdate, remove rows: SQLiteRowRange, affects: SyncUpdateEnd)
|
||||||
|
|
||||||
/// Background process did delete some entries in database that match `affectedDomain`.
|
/// Background process did delete some entries in database that match `affectedDomain`.
|
||||||
/// Update or remove entries from your `dataSource`.
|
/// Update or remove entries from your `dataSource`.
|
||||||
|
|||||||
@@ -19,18 +19,26 @@ extension UITableView {
|
|||||||
/// Returns `true` if this `tableView` is the currently frontmost visible
|
/// Returns `true` if this `tableView` is the currently frontmost visible
|
||||||
var isFrontmost: Bool { window?.isKeyWindow ?? false }
|
var isFrontmost: Bool { window?.isKeyWindow ?? false }
|
||||||
|
|
||||||
|
/// If frontmost window, perform `insertRows()`; If not, perform `reloadData()`
|
||||||
|
func safeInsertRow(_ index: Int, with animation: UITableView.RowAnimation = .automatic) {
|
||||||
|
isFrontmost ? insertRows(at: [IndexPath(row: index)], with: animation) : reloadData()
|
||||||
|
}
|
||||||
|
/// If frontmost window, perform `insertRows()`; If not, perform `reloadData()`
|
||||||
|
func safeInsertRows(_ range: Range<Int>, with animation: UITableView.RowAnimation = .automatic) {
|
||||||
|
isFrontmost ? insertRows(at: range.map {IndexPath(row: $0)}, with: animation) : reloadData()
|
||||||
|
}
|
||||||
/// If frontmost window, perform `deleteRows()`; If not, perform `reloadData()`
|
/// If frontmost window, perform `deleteRows()`; If not, perform `reloadData()`
|
||||||
func safeDeleteRows(_ indices: [Int], with animation: UITableView.RowAnimation = .automatic) {
|
func safeDeleteRows(_ indices: [Int], with animation: UITableView.RowAnimation = .automatic) {
|
||||||
isFrontmost ? deleteRows(at: indices.map {IndexPath(row: $0)}, with: animation) : reloadData()
|
isFrontmost ? deleteRows(at: indices.map {IndexPath(row: $0)}, with: animation) : reloadData()
|
||||||
}
|
}
|
||||||
|
/// If frontmost window, perform `deleteRows()`; If not, perform `reloadData()`
|
||||||
|
func safeDeleteRows(_ range: Range<Int>, with animation: UITableView.RowAnimation = .automatic) {
|
||||||
|
isFrontmost ? deleteRows(at: range.map {IndexPath(row: $0)}, with: animation) : reloadData()
|
||||||
|
}
|
||||||
/// If frontmost window, perform `reloadRows()`; If not, perform `reloadData()`
|
/// If frontmost window, perform `reloadRows()`; If not, perform `reloadData()`
|
||||||
func safeReloadRow(_ index: Int, with animation: UITableView.RowAnimation = .automatic) {
|
func safeReloadRow(_ index: Int, with animation: UITableView.RowAnimation = .automatic) {
|
||||||
isFrontmost ? reloadRows(at: [IndexPath(row: index)], with: animation) : reloadData()
|
isFrontmost ? reloadRows(at: [IndexPath(row: index)], with: animation) : reloadData()
|
||||||
}
|
}
|
||||||
/// If frontmost window, perform `insertRows()`; If not, perform `reloadData()`
|
|
||||||
func safeInsertRow(_ index: Int, with animation: UITableView.RowAnimation = .automatic) {
|
|
||||||
isFrontmost ? insertRows(at: [IndexPath(row: index)], with: animation) : reloadData()
|
|
||||||
}
|
|
||||||
/// If frontmost window, perform `moveRow()`; If not, perform `reloadData()`
|
/// If frontmost window, perform `moveRow()`; If not, perform `reloadData()`
|
||||||
func safeMoveRow(_ from: Int, to: Int) {
|
func safeMoveRow(_ from: Int, to: Int) {
|
||||||
isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData()
|
isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData()
|
||||||
|
|||||||
@@ -42,34 +42,41 @@ extension TVCHostDetails {
|
|||||||
DispatchQueue.main.sync { tableView.reloadData() }
|
DispatchQueue.main.sync { tableView.reloadData() }
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncUpdate(_ _: SyncUpdate, insert rows: SQLiteRowRange) {
|
func syncUpdate(_ _: SyncUpdate, insert rows: SQLiteRowRange, affects: SyncUpdateEnd) {
|
||||||
guard let latest = AppDB?.timesForDomain(fullDomain, range: rows), latest.count > 0 else {
|
guard let latest = AppDB?.timesForDomain(fullDomain, range: rows), latest.count > 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: if filter will be ever editable at this point, we cannot insert at 0
|
// Assuming they are ordered by ts and in descending order
|
||||||
dataSource.insert(contentsOf: latest, at: 0)
|
let range: Range<Int>
|
||||||
DispatchQueue.main.sync {
|
switch affects {
|
||||||
if tableView.isFrontmost {
|
case .Earliest:
|
||||||
let indices = (0..<latest.count).map { IndexPath(row: $0) }
|
range = dataSource.endIndex..<(dataSource.endIndex + latest.count)
|
||||||
tableView.insertRows(at: indices, with: .left)
|
dataSource.append(contentsOf: latest)
|
||||||
} else {
|
case .Latest:
|
||||||
tableView.reloadData()
|
range = dataSource.startIndex..<(dataSource.startIndex + latest.count)
|
||||||
}
|
dataSource.insert(contentsOf: latest, at: 0)
|
||||||
}
|
}
|
||||||
|
DispatchQueue.main.sync { tableView.safeInsertRows(range, with: .left) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncUpdate(_ sender: SyncUpdate, remove _: SQLiteRowRange) {
|
func syncUpdate(_ sender: SyncUpdate, remove _: SQLiteRowRange, affects: SyncUpdateEnd) {
|
||||||
// Assuming they are ordered by ts and in descending order
|
// Assuming they are ordered by ts and in descending order
|
||||||
if let t = sender.tsEarliest, let i = dataSource.lastIndex(where: { $0.ts >= t }), (i+1) < dataSource.count {
|
let range: Range<Int>
|
||||||
let indices = ((i+1)..<dataSource.endIndex).map{ $0 }
|
switch affects {
|
||||||
|
case .Earliest:
|
||||||
|
guard let t = sender.tsEarliest,
|
||||||
|
let i = dataSource.lastIndex(where: { $0.ts >= t }),
|
||||||
|
(i+1) < dataSource.count else { return }
|
||||||
|
range = (i+1)..<dataSource.endIndex
|
||||||
dataSource.removeLast(dataSource.count - (i+1))
|
dataSource.removeLast(dataSource.count - (i+1))
|
||||||
DispatchQueue.main.sync { tableView.safeDeleteRows(indices) }
|
case .Latest:
|
||||||
}
|
guard let t = sender.tsLatest,
|
||||||
if let t = sender.tsLatest, let i = dataSource.firstIndex(where: { $0.ts <= t }), i > 0 {
|
let i = dataSource.firstIndex(where: { $0.ts <= t }),
|
||||||
let indices = (dataSource.startIndex..<i).map{ $0 }
|
i > 0 else { return }
|
||||||
|
range = dataSource.startIndex..<i
|
||||||
dataSource.removeFirst(i)
|
dataSource.removeFirst(i)
|
||||||
DispatchQueue.main.sync { tableView.safeDeleteRows(indices) }
|
|
||||||
}
|
}
|
||||||
|
DispatchQueue.main.sync { tableView.safeDeleteRows(range) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncUpdate(_ sender: SyncUpdate, partialRemove affectedDomain: String) {
|
func syncUpdate(_ sender: SyncUpdate, partialRemove affectedDomain: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user