Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,6 @@ docs/LiveActivityTestPlan.md
CLAUDE.md

node_modules/

# Python helper scripts
__pycache__/
1 change: 1 addition & 0 deletions LoopFollow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@
knownRegions = (
en,
Base,
tr,
);
mainGroup = FC97880B2485969B00A7906C;
packageReferences = (
Expand Down
3 changes: 2 additions & 1 deletion LoopFollow/Alarm/AddAlarm/AddAlarmSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ struct AddAlarmSheet: View {
private func matches(_ type: AlarmType) -> Bool {
let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
guard !query.isEmpty else { return true }
return type.rawValue.localizedCaseInsensitiveContains(query)
return type.displayName.localizedCaseInsensitiveContains(query)
|| type.rawValue.localizedCaseInsensitiveContains(query)
|| type.blurb.localizedCaseInsensitiveContains(query)
|| type.group.rawValue.localizedCaseInsensitiveContains(query)
}
Expand Down
2 changes: 1 addition & 1 deletion LoopFollow/Alarm/AddAlarm/AlarmTile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct AlarmTile: View {
Image(systemName: type.icon)
.font(.title2)
.foregroundColor(.accentColor)
Text(type.rawValue)
Text(type.displayName)
.font(.subheadline)
.multilineTextAlignment(.center)
.lineLimit(2)
Expand Down
60 changes: 33 additions & 27 deletions LoopFollow/Alarm/Alarm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ protocol DayNightDisplayable {

extension DayNightDisplayable where Self: RawRepresentable, Self.RawValue == String {
var displayName: String {
rawValue == "always" ? "Day & Night" : rawValue.capitalized
switch rawValue {
case "always": String(localized: "Day & Night", comment: "Alarm schedule option: at any time of day")
case "day": String(localized: "Day", comment: "Alarm schedule option: daytime only")
case "night": String(localized: "Night", comment: "Alarm schedule option: nighttime only")
case "never": String(localized: "Never", comment: "Alarm schedule option: never")
default: rawValue.capitalized
}
}
}

Expand Down Expand Up @@ -277,7 +283,7 @@ struct Alarm: Identifiable, Codable, Equatable {
}
}()

AlarmManager.shared.sendNotification(title: type.rawValue, actionTitle: snoozeDuration == 0 ? "Acknowledge" : "Snooze")
AlarmManager.shared.sendNotification(title: type.displayName, actionTitle: snoozeDuration == 0 ? "Acknowledge" : "Snooze")

if playSound {
AlarmSound.setSoundFile(soundFile)
Expand All @@ -289,7 +295,7 @@ struct Alarm: Identifiable, Codable, Equatable {

init(type: AlarmType) {
self.type = type
name = type.rawValue
name = type.displayName

switch type {
case .buildExpire:
Expand Down Expand Up @@ -448,30 +454,30 @@ extension AlarmType {

var blurb: String {
switch self {
case .low: return "Alerts when BG goes below a limit."
case .high: return "Alerts when BG rises above a limit."
case .fastDrop: return "Rapid downward BG trend."
case .fastRise: return "Rapid upward BG trend."
case .missedReading: return "No CGM data for X minutes."
case .iob: return "High insulin-on-board."
case .cob: return "High carbs-on-board."
case .missedBolus: return "Carbs without bolus."
case .futureCarbs: return "Reminder when future carbs are due."
case .recBolus: return "Recommended bolus issued."
case .battery: return "Phone battery low."
case .batteryDrop: return "Battery drops quickly."
case .pump: return "Reservoir level low."
case .pumpBattery: return "Pump battery low."
case .pumpChange: return "Pump change due."
case .sensorChange: return "Sensor change due."
case .notLooping: return "Loop hasn’t completed."
case .buildExpire: return "Looping-app build expiring."
case .dbSize: return "Nightscout database filling up."
case .overrideStart: return "Override just started."
case .overrideEnd: return "Override ended."
case .tempTargetStart: return "Temp target started."
case .tempTargetEnd: return "Temp target ended."
case .temporary: return "One-time BG limit alert."
case .low: return String(localized: "Alerts when BG goes below a limit.", comment: "Alarm type blurb")
case .high: return String(localized: "Alerts when BG rises above a limit.", comment: "Alarm type blurb")
case .fastDrop: return String(localized: "Rapid downward BG trend.", comment: "Alarm type blurb")
case .fastRise: return String(localized: "Rapid upward BG trend.", comment: "Alarm type blurb")
case .missedReading: return String(localized: "No CGM data for X minutes.", comment: "Alarm type blurb")
case .iob: return String(localized: "High insulin-on-board.", comment: "Alarm type blurb")
case .cob: return String(localized: "High carbs-on-board.", comment: "Alarm type blurb")
case .missedBolus: return String(localized: "Carbs without bolus.", comment: "Alarm type blurb")
case .futureCarbs: return String(localized: "Reminder when future carbs are due.", comment: "Alarm type blurb")
case .recBolus: return String(localized: "Recommended bolus issued.", comment: "Alarm type blurb")
case .battery: return String(localized: "Phone battery low.", comment: "Alarm type blurb")
case .batteryDrop: return String(localized: "Battery drops quickly.", comment: "Alarm type blurb")
case .pump: return String(localized: "Reservoir level low.", comment: "Alarm type blurb")
case .pumpBattery: return String(localized: "Pump battery low.", comment: "Alarm type blurb")
case .pumpChange: return String(localized: "Pump change due.", comment: "Alarm type blurb")
case .sensorChange: return String(localized: "Sensor change due.", comment: "Alarm type blurb")
case .notLooping: return String(localized: "Loop hasn’t completed.", comment: "Alarm type blurb")
case .buildExpire: return String(localized: "Looping-app build expiring.", comment: "Alarm type blurb")
case .dbSize: return String(localized: "Nightscout database filling up.", comment: "Alarm type blurb")
case .overrideStart: return String(localized: "Override just started.", comment: "Alarm type blurb")
case .overrideEnd: return String(localized: "Override ended.", comment: "Alarm type blurb")
case .tempTargetStart: return String(localized: "Temp target started.", comment: "Alarm type blurb")
case .tempTargetEnd: return String(localized: "Temp target ended.", comment: "Alarm type blurb")
case .temporary: return String(localized: "One-time BG limit alert.", comment: "Alarm type blurb")
}
}
}
2 changes: 1 addition & 1 deletion LoopFollow/Alarm/AlarmEditing/AlarmEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct AlarmEditor: View {
}
}
}
.navigationTitle(alarm.type.rawValue)
.navigationTitle(alarm.type.displayName)
}
.preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ struct AlarmAudioSection: View {
}

struct AlarmEnumMenuPicker<E: CaseIterable & Hashable & DayNightDisplayable>: View {
let title: String
let title: LocalizedStringKey
@Binding var selection: E
var allowed: [E]

Expand All @@ -101,7 +101,7 @@ struct AlarmEnumMenuPicker<E: CaseIterable & Hashable & DayNightDisplayable>: Vi
}

extension AlarmEnumMenuPicker where E: CaseIterable {
init(title: String, selection: Binding<E>) {
init(title: LocalizedStringKey, selection: Binding<E>) {
self.title = title
_selection = selection
allowed = Array(E.allCases)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import SwiftUI

struct AlarmBGLimitSection: View {
// ────────── Public API ──────────
let header: String?
let footer: String?
let header: LocalizedStringKey?
let footer: LocalizedStringKey?
let toggleText: String
let pickerTitle: String
let pickerTitle: LocalizedStringKey
let range: ClosedRange<Double>
let defaultOnValue: Double

@Binding var value: Double?
// ────────────────────────────────

init(
header: String? = nil,
footer: String? = nil,
header: LocalizedStringKey? = nil,
footer: LocalizedStringKey? = nil,
toggleText: String,
pickerTitle: String,
pickerTitle: LocalizedStringKey,
range: ClosedRange<Double>,
defaultOnValue: Double? = nil,
value: Binding<Double?>
Expand Down Expand Up @@ -60,8 +60,8 @@ struct AlarmBGLimitSection: View {

var body: some View {
Section(
header: header.map(Text.init),
footer: footer.map(Text.init)
header: header.map { Text($0) },
footer: footer.map { Text($0) }
) {
Toggle(toggleText, isOn: isOn)

Expand Down
16 changes: 8 additions & 8 deletions LoopFollow/Alarm/AlarmEditing/Components/AlarmBGSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import SwiftUI
struct AlarmBGSection: View {
// MARK: – public parameters

let header: String?
let footer: String?
let title: String
let header: LocalizedStringKey?
let footer: LocalizedStringKey?
let title: LocalizedStringKey
let range: ClosedRange<Double>

// MARK: – underlying optional binding
Expand All @@ -19,9 +19,9 @@ struct AlarmBGSection: View {
// MARK: – designated initialiser

init(
header: String? = nil,
footer: String? = nil,
title: String,
header: LocalizedStringKey? = nil,
footer: LocalizedStringKey? = nil,
title: LocalizedStringKey,
range: ClosedRange<Double>,
value: Binding<Double?>
) {
Expand All @@ -45,8 +45,8 @@ struct AlarmBGSection: View {

var body: some View {
Section(
header: header.map(Text.init),
footer: footer.map(Text.init)
header: header.map { Text($0) },
footer: footer.map { Text($0) }
) {
BGPicker(
title: title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import SwiftUI
struct AlarmStepperSection: View {
// MARK: – public parameters

let header: String?
let footer: String?
let title: String
let header: LocalizedStringKey?
let footer: LocalizedStringKey?
let title: LocalizedStringKey
let range: ClosedRange<Double>
let step: Double
let unitLabel: String?
Expand All @@ -21,9 +21,9 @@ struct AlarmStepperSection: View {
// MARK: – designated initialiser (Double?)

init(
header: String? = nil,
footer: String? = nil,
title: String,
header: LocalizedStringKey? = nil,
footer: LocalizedStringKey? = nil,
title: LocalizedStringKey,
range: ClosedRange<Double>,
step: Double,
unitLabel: String? = nil,
Expand All @@ -42,9 +42,9 @@ struct AlarmStepperSection: View {

/// Same API but for **`Binding<Int?>`** — it bridges to Double internally.
init(
header: String? = nil,
footer: String? = nil,
title: String,
header: LocalizedStringKey? = nil,
footer: LocalizedStringKey? = nil,
title: LocalizedStringKey,
range: ClosedRange<Double>,
step: Double,
unitLabel: String? = nil,
Expand Down Expand Up @@ -90,8 +90,8 @@ struct AlarmStepperSection: View {

var body: some View {
Section(
header: header.map(Text.init),
footer: footer.map(Text.init)
header: header.map { Text($0) },
footer: footer.map { Text($0) }
) {
Stepper(value: nonOptional, in: range, step: step) {
HStack {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SwiftUI

struct InfoBanner: View {
/// Main explanatory text
let text: String
let text: LocalizedStringKey

/// Optional alarm type whose icon you’d like to show.
/// If `nil`, we fall back to the standard “info” symbol.
Expand Down
2 changes: 1 addition & 1 deletion LoopFollow/Alarm/AlarmEditing/Components/SoundFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ enum SoundFile: Hashable, Identifiable {
.replacingOccurrences(of: "_", with: " ")
.replacingOccurrences(of: " ", with: " ")
case let .custom(uuid):
return CustomSoundStore.shared.displayName(for: uuid) ?? "Custom Sound"
return CustomSoundStore.shared.displayName(for: uuid) ?? String(localized: "Custom Sound")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ struct BuildExpireAlarmEditor: View {
var body: some View {
Group {
InfoBanner(
text: "Sends a reminder before the looping-app build you’re following reaches its "
+ "TestFlight or Xcode expiry date. Works with Trio 0.4 and later."
text: "Sends a reminder before the looping-app build you’re following reaches its TestFlight or Xcode expiry date. Works with Trio 0.4 and later."
)
AlarmGeneralSection(alarm: $alarm)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ struct FastRiseAlarmEditor: View {
var body: some View {
Group {
InfoBanner(
text: "Alerts when glucose readings rise rapidly. For example, "
+ "three straight readings each climbing by at least the amount "
+ "you set. Optionally limit alerts to only fire above a certain BG.",
text: "Alerts when glucose readings rise rapidly. For example, three straight readings each climbing by at least the amount you set. Optionally limit alerts to only fire above a certain BG.",
alarmType: alarm.type
)

Expand All @@ -27,8 +25,7 @@ struct FastRiseAlarmEditor: View {

AlarmStepperSection(
header: "Consecutive Rises",
footer: "Number of rises—each meeting the rate above—"
+ "required before an alert fires.",
footer: "Number of rises—each meeting the rate above—required before an alert fires.",
title: "Rises in a row",
range: 1 ... 3,
step: 1,
Expand All @@ -37,8 +34,7 @@ struct FastRiseAlarmEditor: View {

AlarmBGLimitSection(
header: "BG Limit",
footer: "When enabled, this alert only fires if the glucose is "
+ "above the limit you set.",
footer: "When enabled, this alert only fires if the glucose is above the limit you set.",
toggleText: "Use BG Limit",
pickerTitle: "Rising above",
range: 40 ... 300,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ struct FutureCarbsAlarmEditor: View {
var body: some View {
Group {
InfoBanner(
text: "Alerts when a future-dated carb entry's scheduled time arrives — " +
"a reminder to start eating. Use the max lookahead to ignore " +
"fat/protein entries that are typically scheduled further ahead.",
text: "Alerts when a future-dated carb entry's scheduled time arrives — a reminder to start eating. Use the max lookahead to ignore fat/protein entries that are typically scheduled further ahead.",
alarmType: alarm.type
)

AlarmGeneralSection(alarm: $alarm)

AlarmStepperSection(
header: "Max Lookahead",
footer: "Only track carb entries scheduled up to this many minutes " +
"in the future. Entries beyond this window are ignored.",
footer: "Only track carb entries scheduled up to this many minutes in the future. Entries beyond this window are ignored.",
title: "Lookahead",
range: 5 ... 120,
step: 5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ struct HighBgAlarmEditor: View {
var body: some View {
Group {
InfoBanner(
text: "Alerts when glucose stays above the limit "
+ "you set below. Use Persistent if you want to ignore brief spikes."
text: "Alerts when glucose stays above the limit you set below. Use Persistent if you want to ignore brief spikes."
)

AlarmGeneralSection(alarm: $alarm)
Expand All @@ -25,8 +24,7 @@ struct HighBgAlarmEditor: View {

AlarmStepperSection(
header: "Persistent High",
footer: "How long glucose must remain above the threshold before the "
+ "alarm actually fires. Set to 0 for an immediate alert.",
footer: "How long glucose must remain above the threshold before the alarm actually fires. Set to 0 for an immediate alert.",
title: "Persistent for",
range: 0 ... 120,
step: 5,
Expand All @@ -36,8 +34,7 @@ struct HighBgAlarmEditor: View {

Section(
header: Text("FALLING BG"),
footer: Text("Stay silent while BG is falling. The alert only sounds "
+ "when the latest reading is flat or still rising.")
footer: Text("Stay silent while BG is falling. The alert only sounds when the latest reading is flat or still rising.")
) {
Toggle("Skip if BG is falling", isOn: $alarm.suppressIfFalling)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ struct IOBAlarmEditor: View {
var body: some View {
Group {
InfoBanner(
text: "Alerts when insulin-on-board is high, or when several "
+ "boluses in quick succession exceed the limits you set.",
text: "Alerts when insulin-on-board is high, or when several boluses in quick succession exceed the limits you set.",
alarmType: alarm.type
)

Expand Down
Loading