Skip to content
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