Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Omnipod Settings View - Friendly Date/Time & Insulin Delivery Timer #129

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
6 changes: 5 additions & 1 deletion OmniBLE/PumpManager/OmniBLEPumpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -830,8 +830,12 @@ extension OmniBLEPumpManager {
// If we're in the simulator, create a mock PodState
let mockFaultDuringPairing = false
let mockCommsErrorDuringPairing = false
let mockStartDate = Date()
//let mockStartDate = Date.init(timeIntervalSinceNow: -(37 * 60 * 60) - 134) // Active Pod within 72 hour expiration : 37 hours, 2 mins, 14 secs
//let mockStartDate = Date.init(timeIntervalSinceNow: -(74 * 60 * 60) - 134) // Expired Pod within 8 hour grace period : 74 hours, 2 mins, 14 secs
//let mockStartDate = Date.init(timeIntervalSinceNow: -(80 * 60 * 60) - 134) // Expired Pod beyond 8 hour grace period : 80 hours, 2 mins, 14 secs
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .seconds(2)) {
self.jumpStartPod(lotNo: 135601809, lotSeq: 0800525, mockFault: mockFaultDuringPairing)
self.jumpStartPod(lotNo: 135601809, lotSeq: 0800525, startDate: mockStartDate, mockFault: mockFaultDuringPairing)
let fault: DetailedStatus? = self.setStateWithResult({ (state) in
var podState = state.podState
podState?.setupProgress = .priming
Expand Down
140 changes: 125 additions & 15 deletions OmniBLE/PumpManagerUI/ViewModels/OmniBLESettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,70 @@ class OmniBLESettingsViewModel: ObservableObject {

var activatedAtString: String {
if let activatedAt = activatedAt {
return dateFormatter.string(from: activatedAt)
} else {
return "—"
if relDateFormatter.string(from: activatedAt) == absDateFormatter.string(from: activatedAt) {
return altRelFormatter.string(from: activatedAt)
}
return relDateAndTimeFormatter.string(from: activatedAt)
} else {
return "—"
}
}
}


var expiresAtString: String {
if let expiresAt = expiresAt {
return dateFormatter.string(from: expiresAt)
if relDateFormatter.string(from: expiresAt) == absDateFormatter.string(from: expiresAt) {
return altRelFormatter.string(from: expiresAt)
}
return relDateAndTimeFormatter.string(from: expiresAt)
} else {
return "—"
}
}

var serviceTimeRemainingString: String? {
if let serviceTimeRemaining = pumpManager.podServiceTimeRemaining, let serviceTimeRemainingString = timeRemainingFormatter.string(from: serviceTimeRemaining) {
return serviceTimeRemainingString
var deliveryStopsAtString: String {
if let serviceTimeRemaining = pumpManager.podServiceTimeRemaining {
let deliveryStopsAt = Date().addingTimeInterval(serviceTimeRemaining)
if relDateFormatter.string(from: deliveryStopsAt) == absDateFormatter.string(from: deliveryStopsAt) {
return altRelFormatter.string(from: deliveryStopsAt)
}
return relDateAndTimeFormatter.string(from: deliveryStopsAt)
} else {
return nil
return "—"
}
}

var serviceTimeRemainingTI: TimeInterval {
if let serviceTimeRemaining = pumpManager.podServiceTimeRemaining {
return serviceTimeRemaining
}
return 0
}

var serviceTimeRemainingString: String? {
if let serviceTimeRemaining = pumpManager.podServiceTimeRemaining {
if let serviceTimeRemainingString = timeRemainingFormatter.string(from: serviceTimeRemaining) {
return serviceTimeRemainingString
}
}
return nil
}

var insulinTimeRemainingString: String? {
if let insulinTimeRemaining = pumpManager.podServiceTimeRemaining {
let insulinTimeRemainingString = insulinTimeRemainingFormatter.string(from: insulinTimeRemaining)
return insulinTimeRemainingString

}
return nil
}
var insulinTimeExpiredString: String? {
if let insulinTimeExpired = pumpManager.podServiceTimeRemaining {
let insulinTimeExpiredString = insulinTimeExpiredFormatter.localizedString(fromTimeInterval: insulinTimeExpired)
return insulinTimeExpiredString

}
return nil
}

// Expiration reminder date for current pod
@Published var expirationReminderDate: Date?
Expand Down Expand Up @@ -151,7 +194,24 @@ class OmniBLESettingsViewModel: ObservableObject {
return nil
}
}

var podServiceTimeRemainingString: String {
if let serviceTimeRemainingString = serviceTimeRemainingString {
return serviceTimeRemainingString
}
return "-"
}
var insulinServiceTimeRemainingString: String {
if let insulinTimeRemainingString = insulinTimeRemainingString {
return insulinTimeRemainingString
}
return "-"
}
var insulinServiceTimeExpiredString: String {
if let insulinTimeExpiredString = insulinTimeExpiredString {
return insulinTimeExpiredString
}
return "-"
}
var notice: DashSettingsNotice? {
if pumpManager.isClockOffset {
return DashSettingsNotice(
Expand All @@ -170,19 +230,52 @@ class OmniBLESettingsViewModel: ObservableObject {
return false
}
}

let timeFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .none
return dateFormatter
}()

let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .medium
dateFormatter.locale = Locale.current
dateFormatter.doesRelativeDateFormatting = true
return dateFormatter
}()

let timeFormatter: DateFormatter = {
let relDateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .medium
dateFormatter.locale = Locale.current
dateFormatter.doesRelativeDateFormatting = true
return dateFormatter
}()
let absDateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .medium
dateFormatter.locale = Locale.current
dateFormatter.doesRelativeDateFormatting = false
return dateFormatter
}()

let altRelFormatter: DateFormatter = {
let fullDF = DateFormatter()
fullDF.locale = Locale.current
fullDF.setLocalizedDateFormatFromTemplate("E, hh:mm a")
return fullDF
}()

let relDateAndTimeFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .none
dateFormatter.dateStyle = .medium
dateFormatter.locale = Locale.current
dateFormatter.doesRelativeDateFormatting = true
return dateFormatter
}()

Expand All @@ -194,6 +287,23 @@ class OmniBLESettingsViewModel: ObservableObject {
return dateComponentsFormatter
}()

let insulinTimeRemainingFormatter: DateComponentsFormatter = {
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.day, .hour, .minute, .second]
dateComponentsFormatter.maximumUnitCount = 2
dateComponentsFormatter.unitsStyle = .short
dateComponentsFormatter.zeroFormattingBehavior = .dropAll
return dateComponentsFormatter
}()

let insulinTimeExpiredFormatter: RelativeDateTimeFormatter = {
let dateComponentsFormatter = RelativeDateTimeFormatter()
dateComponentsFormatter.locale = Locale.current
dateComponentsFormatter.unitsStyle = .short
dateComponentsFormatter.dateTimeStyle = .numeric
return dateComponentsFormatter
}()

let basalRateFormatter: NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
Expand Down Expand Up @@ -566,7 +676,7 @@ extension OmniBLEPumpManager {
guard let podTimeRemaining = podTimeRemaining else {
return nil;
}
return max(0, Pod.serviceDuration - Pod.nominalPodLife + podTimeRemaining);
return Pod.serviceDuration - Pod.nominalPodLife + podTimeRemaining;
}

private func podDetails(fromPodState podState: PodState, andDeviceName deviceName: String?) -> PodDetails {
Expand Down
66 changes: 59 additions & 7 deletions OmniBLE/PumpManagerUI/Views/OmniBLESettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct OmniBLESettingsView: View {
}

private var minutesRemaining: Int? {
if case .timeRemaining(let remaining, _) = viewModel.lifeState, remaining < .hours(2) {
if case .timeRemaining(let remaining, _) = viewModel.lifeState, remaining < .days(1) {
return Int(remaining.minutes.truncatingRemainder(dividingBy: 60))
}
return nil
Expand Down Expand Up @@ -355,17 +355,69 @@ struct OmniBLESettingsView: View {
.foregroundColor(Color.secondary)
}

HStack {
if let expiresAt = viewModel.expiresAt, expiresAt < Date() {
if let expiresAt = viewModel.expiresAt, expiresAt < Date() {
HStack {
FrameworkLocalText("Pod Expired", comment: "Label for pod expiration row, past tense")
} else {
Spacer()
Text(self.viewModel.expiresAtString)
.foregroundColor(Color.red)
}
} else {
HStack {
FrameworkLocalText("Pod Expires", comment: "Label for pod expiration row")
Spacer()
Text(self.viewModel.expiresAtString)
.foregroundColor(Color.secondary)
}
Spacer()
Text(self.viewModel.expiresAtString)
.foregroundColor(Color.secondary)
}

/*
if let serviceTimeRemainingTI = Optional(viewModel.serviceTimeRemainingTI), serviceTimeRemainingTI < Pod.serviceDuration - Pod.nominalPodLife, serviceTimeRemainingTI > 0 {
HStack {
FrameworkLocalText("Delivery Stoppage Timer", comment: "Label for insulin delivery stoppage timer row")
Spacer()
Text(self.viewModel.podServiceTimeRemainingString)
.foregroundColor(Color.red)
}
}
*/

if let serviceTimeRemainingTI = Optional(viewModel.serviceTimeRemainingTI), serviceTimeRemainingTI <= 0 {
HStack (alignment: .firstTextBaseline){
FrameworkLocalText("Insulin Delivery Stopped", comment: "Label for insulin delivery stop time row, past tense")
Spacer()
VStack(alignment: .trailing) {
Text(self.viewModel.deliveryStopsAtString)
.foregroundColor(Color.red)
.frame(alignment: .trailing)
Text(self.viewModel.insulinServiceTimeExpiredString)
.foregroundColor(Color.red)
.frame(alignment: .trailing)
}
.layoutPriority(1)
}
} else {
HStack(alignment: .firstTextBaseline) {
FrameworkLocalText("Insulin Delivery Stops", comment: "Label for insulin delivery stop time row")
Spacer()
VStack(alignment: .trailing) {
if let serviceTimeRemainingTI = Optional(viewModel.serviceTimeRemainingTI), serviceTimeRemainingTI < Pod.serviceDuration - Pod.nominalPodLife {
Text(self.viewModel.deliveryStopsAtString)
.foregroundColor(Color.red)
.frame(alignment: .trailing)
Text(self.viewModel.insulinServiceTimeRemainingString)
.foregroundColor(Color.red)
.frame(alignment: .trailing)
} else {
Text(self.viewModel.deliveryStopsAtString)
.foregroundColor(Color.secondary)
.frame(alignment: .trailing)
}
}
.layoutPriority(1)
}
}

if let podDetails = self.viewModel.podDetails {
NavigationLink(destination: PodDetailsView(podDetails: podDetails, title: LocalizedString("Pod Details", comment: "title for pod details page"))) {
FrameworkLocalText("Pod Details", comment: "Text for pod details disclosure row")
Expand Down