MinAgoTask.swift 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // LoopFollow
  2. // MinAgoTask.swift
  3. // Created by Jonas Björkert.
  4. import Foundation
  5. import UIKit
  6. extension MainViewController {
  7. func scheduleMinAgoTask(initialDelay: TimeInterval = 1.0) {
  8. let firstRun = Date().addingTimeInterval(initialDelay)
  9. TaskScheduler.shared.scheduleTask(id: .minAgoUpdate, nextRun: firstRun) { [weak self] in
  10. guard let self = self else { return }
  11. self.minAgoTaskAction()
  12. }
  13. }
  14. func minAgoTaskAction() {
  15. guard bgData.count > 0, let lastBG = bgData.last else {
  16. DispatchQueue.main.async { [weak self] in
  17. guard let self = self else { return }
  18. self.MinAgoText.text = ""
  19. Observable.shared.minAgoText.value = ""
  20. Observable.shared.bgText.value = ""
  21. }
  22. TaskScheduler.shared.rescheduleTask(id: .minAgoUpdate, to: Date().addingTimeInterval(1))
  23. return
  24. }
  25. let bgSeconds = lastBG.date
  26. let now = Date()
  27. let secondsAgo = now.timeIntervalSince1970 - bgSeconds
  28. let formatter = DateComponentsFormatter()
  29. formatter.unitsStyle = .positional
  30. formatter.zeroFormattingBehavior = .dropLeading
  31. let shouldDisplaySeconds = secondsAgo >= 270 && secondsAgo < 720 // 4.5 to 12 minutes
  32. if shouldDisplaySeconds {
  33. formatter.allowedUnits = [.minute, .second]
  34. } else {
  35. formatter.allowedUnits = [.minute]
  36. }
  37. let formattedDuration = formatter.string(from: secondsAgo) ?? ""
  38. let minAgoDisplayText = formattedDuration + " min ago"
  39. // Update UI only if the display text has changed
  40. if minAgoDisplayText != Observable.shared.minAgoText.value {
  41. DispatchQueue.main.async { [weak self] in
  42. guard let self = self else { return }
  43. self.MinAgoText.text = minAgoDisplayText
  44. Observable.shared.minAgoText.value = minAgoDisplayText
  45. }
  46. }
  47. let deltaTime = secondsAgo / 60
  48. Observable.shared.bgStale.value = deltaTime >= 12
  49. // Apply strikethrough to BGText based on the staleness of the data
  50. // Also clear badge if bgvalue is stale
  51. let bgTextStr = BGText.text ?? ""
  52. let attributeString = NSMutableAttributedString(string: bgTextStr)
  53. attributeString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: attributeString.length))
  54. if Observable.shared.bgStale.value { // Data is stale
  55. attributeString.addAttribute(.strikethroughColor, value: UIColor.systemRed, range: NSRange(location: 0, length: attributeString.length))
  56. updateBadge(val: 0)
  57. } else { // Data is fresh
  58. attributeString.addAttribute(.strikethroughColor, value: UIColor.clear, range: NSRange(location: 0, length: attributeString.length))
  59. updateBadge(val: Observable.shared.bg.value ?? 0)
  60. }
  61. BGText.attributedText = attributeString
  62. // Determine the next run interval based on the current state
  63. let nextUpdateInterval: TimeInterval
  64. if shouldDisplaySeconds {
  65. // Update every second when showing seconds
  66. nextUpdateInterval = 1.0
  67. } else if secondsAgo >= 240, secondsAgo < 720 {
  68. // Schedule exactly at the transition point to start showing seconds
  69. nextUpdateInterval = 270.0 - secondsAgo
  70. } else {
  71. // Schedule exactly at the transition point to next minute
  72. let secondsToNextMinute = 60.0 - (secondsAgo.truncatingRemainder(dividingBy: 60.0))
  73. nextUpdateInterval = secondsToNextMinute
  74. }
  75. // Ensure the nextUpdateInterval is not negative or too small
  76. let safeNextInterval = max(nextUpdateInterval, 1.0)
  77. TaskScheduler.shared.rescheduleTask(id: .minAgoUpdate, to: Date().addingTimeInterval(safeNextInterval))
  78. }
  79. }