Skip to content

Commit

Permalink
LOOP-1484: Correction ranges in therapy acceptance flow (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
novalegra authored Jul 8, 2020
1 parent 0471b79 commit 01f1a95
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 71 deletions.
12 changes: 8 additions & 4 deletions TidepoolService.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
A9DAAD6A22E7E81E00E76C9F /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9DAAD6922E7E81E00E76C9F /* LoopKitUI.framework */; };
A9DAAD6D22E7EA8F00E76C9F /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DAAD6C22E7EA8F00E76C9F /* IdentifiableClass.swift */; };
A9DAAD6F22E7EA9700E76C9F /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DAAD6E22E7EA9700E76C9F /* NibLoadable.swift */; };
E93BA05E24A14CBA00C5D7E6 /* PrescriptionCodeEntryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93BA05D24A14CBA00C5D7E6 /* PrescriptionCodeEntryViewModel.swift */; };
E93BA05E24A14CBA00C5D7E6 /* PrescriptionReviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93BA05D24A14CBA00C5D7E6 /* PrescriptionReviewViewModel.swift */; };
E93BA06024A15FA800C5D7E6 /* PrescriptionDeviceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93BA05F24A15FA800C5D7E6 /* PrescriptionDeviceView.swift */; };
E93BA06224A29C9C00C5D7E6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E93BA06124A29C9C00C5D7E6 /* Assets.xcassets */; };
E9692172249BC73600D9BE3B /* MockPrescriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9692171249BC73600D9BE3B /* MockPrescriptionManager.swift */; };
Expand All @@ -71,6 +71,7 @@
E96AEB98249C2FF1003797B4 /* PrescriptionCodeEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96AEB97249C2FF1003797B4 /* PrescriptionCodeEntryView.swift */; };
E991F1B724A548580059281B /* AdaptiveKeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E991F1B624A548580059281B /* AdaptiveKeyboardPadding.swift */; };
E991F1BB24A654CC0059281B /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = E991F1BA24A654CC0059281B /* TimeInterval.swift */; };
E991F1C524AAB25C0059281B /* CorrectionRangeReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E991F1C424AAB25C0059281B /* CorrectionRangeReviewView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -177,7 +178,7 @@
A9DAAD6922E7E81E00E76C9F /* LoopKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A9DAAD6C22E7EA8F00E76C9F /* IdentifiableClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifiableClass.swift; sourceTree = "<group>"; };
A9DAAD6E22E7EA9700E76C9F /* NibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NibLoadable.swift; sourceTree = "<group>"; };
E93BA05D24A14CBA00C5D7E6 /* PrescriptionCodeEntryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrescriptionCodeEntryViewModel.swift; sourceTree = "<group>"; };
E93BA05D24A14CBA00C5D7E6 /* PrescriptionReviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrescriptionReviewViewModel.swift; sourceTree = "<group>"; };
E93BA05F24A15FA800C5D7E6 /* PrescriptionDeviceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrescriptionDeviceView.swift; sourceTree = "<group>"; };
E93BA06124A29C9C00C5D7E6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E9692171249BC73600D9BE3B /* MockPrescriptionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrescriptionManager.swift; sourceTree = "<group>"; };
Expand All @@ -186,6 +187,7 @@
E96AEB97249C2FF1003797B4 /* PrescriptionCodeEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrescriptionCodeEntryView.swift; sourceTree = "<group>"; };
E991F1B624A548580059281B /* AdaptiveKeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveKeyboardPadding.swift; sourceTree = "<group>"; };
E991F1BA24A654CC0059281B /* TimeInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInterval.swift; sourceTree = "<group>"; };
E991F1C424AAB25C0059281B /* CorrectionRangeReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CorrectionRangeReviewView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -386,7 +388,7 @@
E93BA05C24A126E000C5D7E6 /* View Models */ = {
isa = PBXGroup;
children = (
E93BA05D24A14CBA00C5D7E6 /* PrescriptionCodeEntryViewModel.swift */,
E93BA05D24A14CBA00C5D7E6 /* PrescriptionReviewViewModel.swift */,
);
path = "View Models";
sourceTree = "<group>";
Expand Down Expand Up @@ -414,6 +416,7 @@
E96AEB97249C2FF1003797B4 /* PrescriptionCodeEntryView.swift */,
E93BA05F24A15FA800C5D7E6 /* PrescriptionDeviceView.swift */,
E991F1B624A548580059281B /* AdaptiveKeyboardPadding.swift */,
E991F1C424AAB25C0059281B /* CorrectionRangeReviewView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -787,7 +790,8 @@
A9DAAD3422E7CA1A00E76C9F /* LocalizedString.swift in Sources */,
A9DAAD3922E7DEE000E76C9F /* TidepoolService+UI.swift in Sources */,
A9DAAD6F22E7EA9700E76C9F /* NibLoadable.swift in Sources */,
E93BA05E24A14CBA00C5D7E6 /* PrescriptionCodeEntryViewModel.swift in Sources */,
E991F1C524AAB25C0059281B /* CorrectionRangeReviewView.swift in Sources */,
E93BA05E24A14CBA00C5D7E6 /* PrescriptionReviewViewModel.swift in Sources */,
A92E770122E9181500591027 /* TidepoolServiceSetupViewController.swift in Sources */,
A9DAAD3B22E7DEF100E76C9F /* TidepoolServiceSettingsViewController.swift in Sources */,
);
Expand Down
2 changes: 1 addition & 1 deletion TidepoolServiceKit/Mocks/MockPrescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public enum BGUnit: String, Codable {
case mgdl
case mmol

var hkUnit: HKUnit {
public var hkUnit: HKUnit {
switch self {
case .mgdl:
return .milligramsPerDeciliter
Expand Down
4 changes: 4 additions & 0 deletions TidepoolServiceKit/TidepoolService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ public final class TidepoolService: Service {
public func completeUpdate() {
serviceDelegate?.serviceDidUpdateState(self)
}

public func saveSettings(settings: TherapySettings) {
serviceDelegate?.serviceHasNewTherapySettings(settings)
}

public func completeDelete() {
guard let session = session else {
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion TidepoolServiceKitUI/Extensions/UIColor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ extension UIColor {
private static func HIGRedColor() -> UIColor {
return UIColor(red: 1, green: 59 / 255, blue: 48 / 255, alpha: 1)
}

}
23 changes: 20 additions & 3 deletions TidepoolServiceKitUI/TidepoolServiceSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

import LoopKitUI
import TidepoolServiceKit

final class TidepoolServiceSettingsViewController: UITableViewController {
/* Added to support prescription flow */
final class TidepoolServiceSettingsViewController: UITableViewController, CompletionDelegate {

private let service: TidepoolService

Expand All @@ -27,7 +27,8 @@ final class TidepoolServiceSettingsViewController: UITableViewController {
super.viewDidLoad()

tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className)


navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(startFlow))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
}

Expand All @@ -47,6 +48,22 @@ final class TidepoolServiceSettingsViewController: UITableViewController {

present(alert, animated: true, completion: completion)
}

@objc private func startFlow() {
let setupViewController = PrescriptionReviewUICoordinator()
setupViewController.completionDelegate = self
setupViewController.onReviewFinished = { [weak service] (settings) in
service?.saveSettings(settings: settings)
}
self.present(setupViewController, animated: true, completion: nil)
}

/* Added to support prescription flow */
func completionNotifyingDidComplete(_ object: CompletionNotifying) {
if let vc = object as? UIViewController, presentedViewController === vc {
dismiss(animated: true, completion: nil)
}
}

private func notifyComplete() {
if let serviceViewController = navigationController as? ServiceViewController {
Expand Down
8 changes: 0 additions & 8 deletions TidepoolServiceKitUI/TidepoolServiceSetupViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ final class TidepoolServiceSetupViewController: UIViewController, TLoginSignupDe
title = service.localizedTitle

navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(startFlow))

var loginSignupViewController = service.tapi.loginSignupViewController()
loginSignupViewController.delegate = self
Expand All @@ -47,13 +46,6 @@ final class TidepoolServiceSetupViewController: UIViewController, TLoginSignupDe
@objc private func cancel() {
notifyComplete()
}


@objc private func startFlow() {
let setupViewController = PrescriptionReviewUICoordinator()
setupViewController.completionDelegate = self
self.present(setupViewController, animated: true, completion: nil)
}

func loginSignup(_ loginSignup: TLoginSignup, didCreateSession session: TSession, completion: @escaping (Error?) -> Void) {
service.completeCreate(withSession: session) { error in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@
import Foundation
import SwiftUI
import LoopKitUI
import LoopKit

enum PrescriptionReviewScreen {
case enterCode
case reviewDevices
case correctionRangeInfo
case correctionRangeEditor

func next() -> PrescriptionReviewScreen? {
switch self {
case .enterCode:
return .reviewDevices
case .reviewDevices:
return .correctionRangeInfo
case .correctionRangeInfo:
return .correctionRangeEditor
case .correctionRangeEditor:
return nil
}
}
Expand All @@ -27,14 +34,14 @@ enum PrescriptionReviewScreen {
class PrescriptionReviewUICoordinator: UINavigationController, CompletionNotifying, UINavigationControllerDelegate {
var screenStack = [PrescriptionReviewScreen]()
weak var completionDelegate: CompletionDelegate?

let viewModel = PrescriptionCodeEntryViewModel()
var onReviewFinished: ((TherapySettings) -> Void)?

let viewModel = PrescriptionReviewViewModel(settings: TherapySettings())

var currentScreen: PrescriptionReviewScreen {
return screenStack.last!
}

// TODO: create delegate so we can add settings to LoopDataManager

init() {
super.init(navigationBarClass: UINavigationBar.self, toolbarClass: UIToolbar.self)
}
Expand All @@ -59,21 +66,47 @@ class PrescriptionReviewUICoordinator: UINavigationController, CompletionNotifyi
self?.stepFinished()
}
let view = PrescriptionCodeEntryView(viewModel: viewModel)
return DismissibleHostingController(rootView: view)
let hostedView = DismissibleHostingController(rootView: view)
hostedView.title = LocalizedString("Your Settings", comment: "Navigation view title")
return hostedView
case .reviewDevices:
viewModel.didFinishStep = { [weak self] in
self?.stepFinished()
}
guard let prescription = viewModel.prescription else {
// Go back to code entry step if we don't have prescription
let view = PrescriptionCodeEntryView(viewModel: viewModel)
return DismissibleHostingController(rootView: view)
return restartFlow()
}
let view = PrescriptionDeviceView(viewModel: viewModel, prescription: prescription)
return DismissibleHostingController(rootView: view)
let hostedView = DismissibleHostingController(rootView: view)
hostedView.title = LocalizedString("Review your settings", comment: "Navigation view title")
return hostedView
case .correctionRangeInfo:
let exiting: (() -> Void) = { [weak self] in
self?.stepFinished()
}
let view = CorrectionRangeInformationView(onExit: exiting)
let hostedView = DismissibleHostingController(rootView: view)
hostedView.title = LocalizedString("Correction Range", comment: "Title for correction range informational screen")
return hostedView
case .correctionRangeEditor:
guard let prescription = viewModel.prescription else {
// Go back to code entry step if we don't have prescription
return restartFlow()
}
let view = CorrectionRangeReviewView(model: viewModel, prescription: prescription)
let hostedView = DismissibleHostingController(rootView: view)
hostedView.navigationItem.largeTitleDisplayMode = .never // fix for jumping
return hostedView
}
}

private func restartFlow() -> UIViewController {
screenStack = [.enterCode]
let view = PrescriptionCodeEntryView(viewModel: viewModel)
return DismissibleHostingController(rootView: view)
}

public func navigationController(_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool) {
Expand All @@ -94,6 +127,7 @@ class PrescriptionReviewUICoordinator: UINavigationController, CompletionNotifyi
setViewControllers([viewController], animated: false)
}

// TODO: have separate flow for cancelling
private func setupCanceled() {
completionDelegate?.completionNotifyingDidComplete(self)
}
Expand All @@ -102,6 +136,9 @@ class PrescriptionReviewUICoordinator: UINavigationController, CompletionNotifyi
if let nextStep = currentScreen.next() {
navigate(to: nextStep)
} else {
if let settingDelegate = onReviewFinished {
settingDelegate(viewModel.settings)
}
completionDelegate?.completionNotifyingDidComplete(self)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@

import Foundation
import TidepoolServiceKit
import LoopKit

class PrescriptionCodeEntryViewModel: ObservableObject {
class PrescriptionReviewViewModel: ObservableObject {
var didFinishStep: (() -> Void)
var didCancel: (() -> Void)?

var prescription: MockPrescription?
let prescriptionCodeLength = 4

init(finishedStepHandler: @escaping () -> Void = { }) {
var settings: TherapySettings

init(finishedStepHandler: @escaping () -> Void = { },
settings: TherapySettings) {
self.didFinishStep = finishedStepHandler
self.settings = settings
}

func entryNavigation(success: Bool) {
Expand Down Expand Up @@ -49,4 +54,8 @@ class PrescriptionCodeEntryViewModel: ObservableObject {
}
}
}

func saveCorrectionRange(range: GlucoseRangeSchedule) {
settings.glucoseTargetRangeSchedule = range
}
}
39 changes: 39 additions & 0 deletions TidepoolServiceKitUI/Views/CorrectionRangeReviewView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// CorrectionRangeReviewView.swift
// TidepoolServiceKitUI
//
// Created by Anna Quinlan on 6/29/20.
// Copyright © 2020 LoopKit Authors. All rights reserved.
//
import SwiftUI
import LoopKitUI
import LoopKit
import HealthKit
import TidepoolServiceKit


struct CorrectionRangeReviewView: View {
@ObservedObject var viewModel: PrescriptionReviewViewModel
let prescription: MockPrescription

init(
model: PrescriptionReviewViewModel,
prescription: MockPrescription
) {
self.viewModel = model
self.prescription = prescription
}

var body: some View {
CorrectionRangeScheduleEditor(
schedule: prescription.glucoseTargetRangeSchedule,
unit: prescription.bloodGlucoseUnit.hkUnit,
minValue: prescription.suspendThreshold.quantity,
onSave: { newSchedule in
self.viewModel.saveCorrectionRange(range: newSchedule)
self.viewModel.didFinishStep()
},
mode: .flow
)
}
}
Loading

0 comments on commit 01f1a95

Please sign in to comment.