Skip to content

Commit

Permalink
Improve item fields editing
Browse files Browse the repository at this point in the history
Delay item fields editing in action handler instead of debouncing row
text fields
Update collection view snapshot when individual rows change
  • Loading branch information
mvasilak committed Jan 17, 2025
1 parent 704152c commit 20126a8
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 176 deletions.
62 changes: 37 additions & 25 deletions Zotero/Controllers/BackgroundTimer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,63 @@
import Foundation

final class BackgroundTimer {
private enum State {
enum State {
case suspended
case resumed
}

private let timeInterval: DispatchTimeInterval
private let queue: DispatchQueue
private var timer: DispatchSourceTimer?
private(set) var startTime: DispatchTime?

var eventHandler: (() -> Void)?
private var state: State = .suspended
private lazy var timer: DispatchSourceTimer = {
let t = DispatchSource.makeTimerSource(flags: [], queue: self.queue)
t.schedule(deadline: .now() + self.timeInterval, repeating: 0)
t.setEventHandler(handler: { [weak self] in
self?.eventHandler?()
self?.suspend()
})
return t
}()
private(set) var state: State = .suspended

init(timeInterval: DispatchTimeInterval, queue: DispatchQueue) {
self.timeInterval = timeInterval
self.queue = queue
}

deinit {
self.timer.setEventHandler {}
self.timer.cancel()
/*
If the timer is suspended, calling cancel without resuming
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
*/
self.resume()
self.eventHandler = nil
invalidate()
}

func resume() {
guard self.state != .resumed else { return }
self.state = .resumed
self.timer.resume()
guard state != .resumed else { return }
state = .resumed
timer = timer ?? createTimer()

timer?.resume()
}

func suspend() {
guard self.state != .suspended else { return }
self.state = .suspended
self.timer.suspend()
guard let timer, state != .suspended else { return }
state = .suspended
timer.suspend()
}

func invalidate() {

This comment has been minimized.

Copy link
@michalrentka

michalrentka Jan 24, 2025

Contributor

Do you call this invalidate() function anywhere from outside? Otherwise I don't see a reason to move this to separate public function from deinit.

guard let timer else { return }
timer.setEventHandler {}
timer.cancel()
/*
If the timer is suspended, calling cancel without resuming
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
*/
resume()
eventHandler = nil
}

private func createTimer() -> DispatchSourceTimer {
let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
let now = DispatchTime.now()
startTime = now
timer.schedule(deadline: now + timeInterval, repeating: 0)
timer.setEventHandler(handler: { [weak self] in
self?.eventHandler?()
self?.suspend()
})
return timer
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ struct ItemDetailState: ViewModelState {

enum TableViewReloadType {
case row(ItemDetailCollectionViewHandler.Row)
case rows([ItemDetailCollectionViewHandler.Row])
case section(ItemDetailCollectionViewHandler.Section)
}

Expand Down
Loading

0 comments on commit 20126a8

Please sign in to comment.