diff --git a/ProjectClock/AddAlarmViewController.swift b/ProjectClock/AddAlarmViewController.swift index 44c06ee..22e2017 100644 --- a/ProjectClock/AddAlarmViewController.swift +++ b/ProjectClock/AddAlarmViewController.swift @@ -9,6 +9,74 @@ import UIKit class AddAlarmViewController: UIViewController { +<<<<<<< Updated upstream +======= + // Editing variables + var alarmCell: AlarmTableCell? = nil + var editMode: Bool { alarmCell != nil } + var originalTime: String = "" + + override func viewDidLoad() + { + // End edit on return + alarmNameTextField.delegate = self + + // Load alarm to edit if in edit mode + if let alarmCell = alarmCell + { + // Toggle editing mode + viewTitle.text = "Edit Alarm" + + // Convert string to Date + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "h:mma" + let date = dateFormatter.date(from: "\(alarmCell.time.text!)\(alarmCell.ampm.text!)") + + // Set all the original values to be edited + timePicker.date = date! + originalTime = String(dateFormatter.string(from: date!).dropLast(2)) + + // Toggle proper repeats + if let repeats = alarmCell.repeatText.text { + if repeats == "Repeats: Weekdays" { + repeatWeekdaysSwitch.isOn = true + repeatWeekendsSwitch.isOn = false + } else if repeats == "Repeats: Weekends" { + repeatWeekendsSwitch.isOn = true + repeatWeekdaysSwitch.isOn = false + } else if repeats == "Repeats: Daily" { + repeatWeekdaysSwitch.isOn = true + repeatWeekendsSwitch.isOn = true + } else { + repeatWeekendsSwitch.isOn = false + repeatWeekdaysSwitch.isOn = false + } + } + + alarmNameTextField.text = String(alarmCell.descriptionText.text!.dropFirst(2)) + updateETA() + + // Sets the WVM + if let wvm = alarmCell.wvmText.text { + for index in 0...wvms.count-1 { + if wvm == wvms[index].name { + wvmPicker.selectRow(index, inComponent: 0, animated: true) + } + } + } + + //Sets alarm tone + if let toneName = alarmCell.toneLabel.text { + for index in 0...ringtones.count-1 { + if toneName == ringtones[index].name { + ringtonePicker.selectRow(index, inComponent: 0, animated: true) + } + } + } + } + } + +>>>>>>> Stashed changes // UI: Make scroll view scrollable @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var scrollViewInner: UIView! @@ -24,7 +92,32 @@ class AddAlarmViewController: UIViewController override func viewDidLoad() { +<<<<<<< Updated upstream super.viewDidLoad() +======= + let (h, m, _) = timePicker.date.getHMS() + + // Create the alarm + let alarm = Alarm(hour: h, minute: m, + text: alarmNameTextField.text ?? "Alarm", + wakeMethod: wvms[wvmPicker.selectedRow(inComponent: 0)], + lastActivate: Date(), alarmTone: ringtones[ringtonePicker.selectedRow(inComponent: 0)].tone, toneName: ringtones[ringtonePicker.selectedRow(inComponent: 0)].name) + + // Set alarm.repeats to correspond with what the user selects + (0...6).forEach { alarm.repeats[$0] = false } + if repeatWeekdaysSwitch.isOn { (1...5).forEach { alarm.repeats[$0] = true } } + if repeatWeekendsSwitch.isOn { [0, 6].forEach { alarm.repeats[$0] = true } } + + return alarm + } + + /** + Dynamically the ETA label for the alarm + */ + func updateETA() { + let timeTill = createAlarm().nextActivate!.timeIntervalSince(Date()).str() + timeTillAlarmLabel.text = "Going off in \(timeTill)" +>>>>>>> Stashed changes } } diff --git a/ProjectClock/AlarmViewController.swift b/ProjectClock/AlarmViewController.swift index 17b0dfd..b27ddbe 100644 --- a/ProjectClock/AlarmViewController.swift +++ b/ProjectClock/AlarmViewController.swift @@ -60,10 +60,72 @@ class AlarmTableCell: UITableViewCell @IBOutlet weak var enable: UISwitch! @IBOutlet weak var repeatText: UILabel! @IBOutlet weak var goingOffText: UILabel! +<<<<<<< Updated upstream +======= + @IBOutlet weak var wvmText: UILabel! + @IBOutlet weak var toneLabel: UILabel! + +>>>>>>> Stashed changes /// Update information on the cell to information in the alarm object func setData(_ alarm: Alarm) { descriptionText.text = "- " + alarm.text +<<<<<<< Updated upstream +======= + enable.isOn = alarm.enabled + wvmText.text = alarm.wakeMethod.name + toneLabel.text = alarm.toneName + + // Display Hour, Minute, and AM or PM + ampm.text = alarm.hour < 12 || alarm.hour == 24 ? "AM" : "PM" + var hour = alarm.hour <= 12 ? alarm.hour : alarm.hour - 12 + hour = alarm.hour == 0 ? 12 : hour + time.text = String(format: "%i:%02i", hour, alarm.minute) + + // displays the specific days alarm is activated + let daysDict = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"] + var daysActive : [String] = [] + if alarm.oneTime {repeatText.text = "One-time Alarm"} + else { + for (index, element) in alarm.repeats.enumerated() { + if element { + daysActive.append(daysDict[index]) + } + } + if daysDict == daysActive { + repeatText.text = "Repeats: Daily" + } else if daysActive == ["Sun", "Sat"] { + repeatText.text = "Repeats: Weekends" + } else if daysActive == ["Mon", "Tues", "Wed", "Thurs", "Fri"] { + repeatText.text = "Repeats: Weekdays" + } else { + repeatText.text = daysActive.joined(separator: ", ") + } + } + + updateActivationTime() + } + + func updateActivationTime() + { + // Show next activation date + if alarm.enabled, let n = alarm.nextActivate { + goingOffText.text = "(Going off in \(n.timeIntervalSince(Date()).str()))" + } + else { + goingOffText.text = "" + } + } + + /** + Called when the user switches the switch + */ + @IBAction func switchChange(_ sender: Any) + { + Alarms.fromLocal().apply { + $0.list.first { $0.hour == self.alarm.hour && $0.minute == self.alarm.minute }?.enabled = enable.isOn + }.localSave() +>>>>>>> Stashed changes } } diff --git a/ProjectClock/Base.lproj/Main.storyboard b/ProjectClock/Base.lproj/Main.storyboard index c30afe5..88ed345 100644 --- a/ProjectClock/Base.lproj/Main.storyboard +++ b/ProjectClock/Base.lproj/Main.storyboard @@ -67,10 +67,20 @@ +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes @@ -94,6 +104,12 @@ +<<<<<<< Updated upstream +======= + + + +>>>>>>> Stashed changes @@ -583,6 +599,572 @@ +<<<<<<< Updated upstream +======= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>>>>>>> Stashed changes diff --git a/ProjectClock/Models.swift b/ProjectClock/Models.swift index cdce614..78a39ec 100644 --- a/ProjectClock/Models.swift +++ b/ProjectClock/Models.swift @@ -11,6 +11,171 @@ struct User: Decodable { var id: Int var name: String +<<<<<<< Updated upstream var email: String var pass: String +======= + var members: String + // And a hidden field: admin pin + + var membersList: [String] { members.csv } + + /// Save family to local storage + func localSave() + { + localStorage.setValue(JSON.stringify(self)!, forKey: "family") + } + + /// Read family object from local storage + static func fromLocal() -> Family? + { + guard let f = localStorage.string(forKey: "family") else { return nil } + return JSON.parse(Family.self, f) + } +} + +struct WVM: Codable +{ + let name: String + let desc: String +} + + +let wvms = [ + WVM(name: "Shake", desc: "Shake your phone... aggresively!"), + WVM(name: "Math 1", desc: "Easy math expression"), + WVM(name: "Math 2", desc: "Medium math expression"), + WVM(name: "Math 3", desc: "Hard math expression"), + WVM(name: "Factor", desc: "Factor a binomial"), + WVM(name: "RPS", desc: "Win a game of rock paper scissors"), + //WVM(name: "Smash", desc: "It'll never turn off"), + //WVM(name: "Walk", desc: "Walk a few steps"), + //WVM(name: "Jump", desc: "Make a few jumps") +] + + +struct Tone: Codable{ + + let name: String + let tone: SystemSoundID + +} + +let ringtones = [ + Tone(name: "News Flash", tone: SystemSoundID(1028)), + Tone(name: "Sherwood Forest", tone: SystemSoundID(1030)), + Tone(name: "Ladder", tone: SystemSoundID(1326)), + Tone(name: "Minuet", tone: SystemSoundID(1327)), + Tone(name: "Tock", tone: SystemSoundID(1306)), + Tone(name: "Bloom", tone: SystemSoundID(1321)), + Tone(name: "Calypso", tone: SystemSoundID(1322)), + Tone(name: "Train", tone: SystemSoundID(1323)), + Tone(name: "Fanfare", tone: SystemSoundID(1325)) +] + +class Alarm: Codable, Equatable +{ + static func == (lhs: Alarm, rhs: Alarm) -> Bool { + return lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.text == rhs.text && + lhs.alarmTone == rhs.alarmTone && lhs.repeats == rhs.repeats + } + + var enabled: Bool + var hour: Int // Hour (24) + var minute: Int + var text: String + var wakeMethod: WVM + var alarmTone: SystemSoundID + var notificationID: String + var toneName: String + + /// What days does it repeat (Sun, Mon, Tue, Wed, Thu, Fri, Sat) + var repeats: [Bool] + + /// When is the last time that the alarm went off + var lastActivate: Date + + /// Constructor + init(enabled: Bool = true, + hour: Int, minute: Int, text: String, wakeMethod: WVM, + repeats: [Bool] = [false, true, true, true, true, true, false], + lastActivate: Date = Date(), + alarmTone: SystemSoundID = ringtones[0].tone, + toneName: String = "" + + ) + { + self.enabled = enabled + self.hour = hour + self.minute = minute + self.text = text + self.wakeMethod = wakeMethod + self.repeats = repeats + self.lastActivate = lastActivate + self.alarmTone = alarmTone + self.notificationID = "notification.id.\(Int.random(in: 1...Int.max))" + self.toneName = toneName + } + + /// Does it automatically disable after activating once + var oneTime: Bool { repeats.allSatisfy { !$0 } } + + /// Get time in h:mm format + var timeText: String { String(format: "%i:%02i", hour, minute) } + + /// When should the alarm activate next since lastActivate? + var nextActivate: Date? + { + let (y, m, d) = lastActivate.getYMD() + let (nh, nm, _) = lastActivate.getHMS() + + // Create activation date + var date = Date.create(y, m, d, hour, minute) + + // If it will activate tomorrow + if nh > hour || (nh == hour && nm >= minute) { date = date.added(.day, 1) } + + // If it's one-time, don't have to check for repeating date + if oneTime { return date } + + // Make sure it's repeating + guard (repeats.contains { $0 }) else { return nil } + + // If the day is not one of the "repeat" days, keep adding 1 until it is + while !repeats[date.get(.weekday) - 1] { date = date.added(.day, 1) } + + return date + } +} + +class Alarms: Codable +{ + var list: [Alarm] = [] + + /// Save alarms to local storage + func localSave() + { + list.sort { ($0.hour * 60 + $0.minute) < ($1.hour * 60 + $1.minute) } + localStorage.setValue(JSON.stringify(list)!, forKey: "alarms") + + // Reload table view + if let table = AlarmViewController.staticTable { table.reloadData() } + } + + /// Read alarms from local storage + func localRead() { list = JSON.parse([Alarm].self, localStorage.string(forKey: "alarms")!)! } + + /// Read an alarm object from local storage + static func fromLocal() -> Alarms { return Alarms().apply { $0.localRead() } } + + /// Get enabled alarms + var listEnabled: [Alarm] { return list.filter { $0.enabled } } + + /// Get alarms that should be activating now + var listActivating: [Alarm] + { + let now = Date() + return listEnabled.filter { guard let n = $0.nextActivate else { return false }; return n < now } + } +>>>>>>> Stashed changes }