From ebe4dbec80c0d4fa57a1f1ab7f3336763342c034 Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Mon, 30 Mar 2026 21:38:30 -0700 Subject: [PATCH 01/27] moved stylesheet into style.js --- AlarmApp/style.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 AlarmApp/style.js diff --git a/AlarmApp/style.js b/AlarmApp/style.js new file mode 100644 index 0000000..7ef90d9 --- /dev/null +++ b/AlarmApp/style.js @@ -0,0 +1,41 @@ + + +const styles = StyleSheet.create({ + container: { flex: 1, padding: 20, backgroundColor: '#fff' }, + title: { fontSize: 28, fontWeight: 'bold', marginVertical: 20, textAlign: 'center' }, + alarmItem: { + flexDirection: 'row', + alignItems: 'center', + padding: 12, + borderBottomWidth: 1, + borderColor: '#ddd', + }, + alarmText: { marginLeft: 12, fontSize: 16, flex: 1 }, + emptyText: { textAlign: 'center', marginTop: 40, color: '#888', fontSize: 16 }, + summary: { + marginVertical: 24, + padding: 16, + backgroundColor: '#f8f9fa', + borderRadius: 12, + borderWidth: 1, + borderColor: '#e0e0e0', + }, + summaryLabel: { fontWeight: 'bold', marginTop: 12, marginBottom: 4 }, + timeText: { fontSize: 18 }, + clickable: { color: '#2196F3', marginVertical: 4, fontWeight: '500' }, + intervalPicker: { + marginTop: 20, + padding: 20, + backgroundColor: '#f0f0f0', + borderRadius: 12, + }, + intervalOption: { + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: '#ddd', + alignItems: 'center', + }, +}); + + +export default styles; \ No newline at end of file From 461144668a9a2d32f9e00ef59b308773da68de88 Mon Sep 17 00:00:00 2001 From: Tristen Date: Wed, 1 Apr 2026 23:01:11 -0700 Subject: [PATCH 02/27] added new alarm class, C-like typedefs, AlarmSet->ALARM class --- AlarmApp/App.tsx | 63 +++++++++++++++++++++++++++++++-------- AlarmApp/alarm.tsx | 74 ++++++++++++++++++++++++++++++++++++++++++++++ AlarmApp/types.tsx | 5 ++++ 3 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 AlarmApp/alarm.tsx create mode 100644 AlarmApp/types.tsx diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index 7ac8944..c7ddb68 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -1,3 +1,6 @@ +// ------------------------------------ // +// IMPORTS // +// ------------------------------------ // import React, { useState, useEffect } from 'react'; import { View, @@ -12,8 +15,16 @@ import { } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; -import styles from "./styles.js" +// local +import styles from "./styles.js"; +import { + DATE, + ALARM_INTERFACE, + ALARM, + generate_alarm_id +} from "./alarm.tsx"; +/* interface AlarmSet { id: string; start: string; @@ -22,22 +33,39 @@ interface AlarmSet { count: number; // num of alarms active: boolean; } +*/ + export default function App() { - const [alarms, setAlarms] = useState([]); + /* + const [alarms, setAlarms] = useState([]); const [startTime, setStartTime] = useState(new Date()); - const [endTime, setEndTime] = useState(() => { + const [endTime, setEndTime] = useState(() => { const d = new Date(); d.setHours(d.getHours() + 1); return d; }); - const [intervalMinutes, setIntervalMinutes] = useState(10); - const [showStartPicker, setShowStartPicker] = useState(false); - const [showEndPicker, setShowEndPicker] = useState(false); + const [intervalMinutes, setIntervalMinutes] = useState(10); + const [showStartPicker, setShowStartPicker] = useState(false); + const [showEndPicker, setShowEndPicker] = useState(false); const [showIntervalPicker, setShowIntervalPicker] = useState(false); + */ - //guard against overlapping alarms - const lastFiredRef = React.useRef>({}); + const [alarms, setAlarms] = useState([]); + const [startTime, setStartTime] = useState(new Date()); + const [endTime, setEndTime] = useState(() => { + const d = new Date(); + d.setHours(d.getHours() + 1); + return d; + }); + const [intervalMinutes, setIntervalMinutes] = useState(10); + const [showStartPicker, setShowStartPicker] = useState(false); + const [showEndPicker, setShowEndPicker] = useState(false); + const [showIntervalPicker, setShowIntervalPicker] = useState(false); + + + //guard against overlapping alarms + const lastFiredRef = React.useRef>({}); // check every second useEffect(() => { @@ -51,7 +79,9 @@ export default function App() { //fire only once per minute to avoid overlapping alarms if(now.getSeconds() !== 0) continue; - if(nowStr === set.end){ + // set end to string + const set_end_str = set.end.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + if(nowStr === set_end_str){ //checks the date so that alarm can fire each day const minuteKey = `${now.toDateString()} ${now.getHours()}:${now.getMinutes()}`; @@ -84,6 +114,7 @@ export default function App() { } // {/*set alarm*/} //should not be in CreateIntervalAlarms + /* const newAlarmSet: AlarmSet = { id: Date.now().toString(), start: startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), @@ -92,6 +123,14 @@ export default function App() { count, active: true, }; + */ + const newAlarmSet: ALARM = { + id: generate_alarm_id(), + start: new DATE(new Date()), + end: new DATE(new Date()), + min_interval: intervalMinutes, + active: true + } setAlarms((prev) => [...prev, newAlarmSet]); Alert.alert('Alarms Created!', `${count} alarms would be scheduled!`); @@ -152,9 +191,9 @@ const confirmDeleteAlarmSet = (id: string) => { /> {/* summarized alarm text */} - Start Time: {item.start} {'\n'} - End Time: {item.end} {'\n'} - Interval: {item.interval} min + Start Time: {item.start.toDigitTime()} {'\n'} + End Time: {item.end.toDigitTime()} {'\n'} + Interval: {item.min_interval} min confirmDeleteAlarmSet(item.id)} diff --git a/AlarmApp/alarm.tsx b/AlarmApp/alarm.tsx new file mode 100644 index 0000000..d9af4c7 --- /dev/null +++ b/AlarmApp/alarm.tsx @@ -0,0 +1,74 @@ + +// ------------------------------------ // +// IMPORTS // +// ------------------------------------ // +//TODO: import Sound from "./Sound.tsx" + + +// ------------------------------------ // +// TYPE DEFINITIONS // +// ------------------------------------ // +import type { + int, + bool +} from "./types.tsx"; + +type ID = string; +type MINUTE = int; + + +// ------------------------------------ // +// INTERFACE & CLASS // +// ------------------------------------ // +export class DATE extends Date { + date: Date; + + constructor(date: Date){ + super(); + + this.date = date; + } + + toDigitTime() : string { + return this.date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + } +} + + + +export interface ALARM_INTERFACE { + id: ID; + start: DATE; + end: DATE; + min_interval: MINUTE; + active: bool; +} + +export class ALARM implements ALARM_INTERFACE { + // members + id: ID; + start: DATE; + end: DATE; + min_interval: MINUTE; + active: bool; + //TODO: add sound variable + + // functions + constructor(times: Date[], interval: MINUTE, is_active: bool) { + this.id = generate_alarm_id(); // function creates new alarm id + this.start = new DATE(times[0]); + this.end = new DATE(times[1]); + this.min_interval = interval; + this.active = false; + } +} + + +// ------------------------------------ // +// FUNCTIONS // +// ------------------------------------ // +let alarm_id = 0; +export function generate_alarm_id(): string { + alarm_id++; + return alarm_id.toString(); +} \ No newline at end of file diff --git a/AlarmApp/types.tsx b/AlarmApp/types.tsx new file mode 100644 index 0000000..3b4b46b --- /dev/null +++ b/AlarmApp/types.tsx @@ -0,0 +1,5 @@ +// +// C/C++ like types +// +export type bool = boolean; +export type int = number; \ No newline at end of file From cf11cc914c4946ab580df27637de8982beb0d591 Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Thu, 2 Apr 2026 00:29:54 -0700 Subject: [PATCH 03/27] updated to use ALARM class instead of interface, using custom DATE instead of Date --- AlarmApp/App.tsx | 94 +++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index c7ddb68..87efae7 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -15,46 +15,24 @@ import { } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; + // local import styles from "./styles.js"; import { DATE, ALARM_INTERFACE, ALARM, - generate_alarm_id + generate_alarm_id, + generate_max_alarm_count, + generate_current_alarm_count } from "./alarm.tsx"; -/* -interface AlarmSet { - id: string; - start: string; - end: string; - interval: number; // minutes - count: number; // num of alarms - active: boolean; -} -*/ - export default function App() { - /* - const [alarms, setAlarms] = useState([]); - const [startTime, setStartTime] = useState(new Date()); - const [endTime, setEndTime] = useState(() => { - const d = new Date(); - d.setHours(d.getHours() + 1); - return d; - }); - const [intervalMinutes, setIntervalMinutes] = useState(10); - const [showStartPicker, setShowStartPicker] = useState(false); - const [showEndPicker, setShowEndPicker] = useState(false); - const [showIntervalPicker, setShowIntervalPicker] = useState(false); - */ - const [alarms, setAlarms] = useState([]); - const [startTime, setStartTime] = useState(new Date()); - const [endTime, setEndTime] = useState(() => { - const d = new Date(); + const [startTime, setStartTime] = useState(new DATE()); + const [endTime, setEndTime] = useState(() => { + const d = new DATE(); d.setHours(d.getHours() + 1); return d; }); @@ -76,13 +54,22 @@ export default function App() { for(const set of alarms){ if(!set.active) continue; + console.log(set); + //fire only once per minute to avoid overlapping alarms if(now.getSeconds() !== 0) continue; + console.log("seconds == 0") + // set end to string - const set_end_str = set.end.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const set_end_str = set.end.toDigitTime(); + + console.log(nowStr, set_end_str) + if(nowStr === set_end_str){ + console.log("alarm ringing..."); + //checks the date so that alarm can fire each day const minuteKey = `${now.toDateString()} ${now.getHours()}:${now.getMinutes()}`; @@ -103,39 +90,16 @@ export default function App() { },[alarms]); const CreateIntervalAlarms = () => { - let current = new Date(startTime); - const end = new Date(endTime); - const intervalMs = intervalMinutes * 60 * 1000; - let count = 0; - - while (current <= end) { - count++; - current = new Date(current.getTime() + intervalMs); - } - - // {/*set alarm*/} //should not be in CreateIntervalAlarms - /* - const newAlarmSet: AlarmSet = { - id: Date.now().toString(), - start: startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - end: endTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - interval: intervalMinutes, - count, - active: true, - }; - */ - const newAlarmSet: ALARM = { - id: generate_alarm_id(), - start: new DATE(new Date()), - end: new DATE(new Date()), - min_interval: intervalMinutes, - active: true - } - - setAlarms((prev) => [...prev, newAlarmSet]); - Alert.alert('Alarms Created!', `${count} alarms would be scheduled!`); + const times = [startTime, endTime]; + const interval = intervalMinutes; + + const alarm = new ALARM(times, interval, true); + + setAlarms((prev) => [...prev, alarm]); + //Alert.alert('Alarms Created!', `${count} alarms would be scheduled!`); }; + //TODO: FIX const toggleAlarmSet = (id: string) => { setAlarms((prev) => prev.map((a) => (a.id === id ? { ...a, active: !a.active } : a)) @@ -210,7 +174,7 @@ const confirmDeleteAlarmSet = (id: string) => { Start Time - {startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + {startTime.toDigitTime()} setShowStartPicker(true)}> Change Start @@ -218,7 +182,7 @@ const confirmDeleteAlarmSet = (id: string) => { {/* end time picker */} End Time - {endTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + {endTime.toDigitTime()} setShowEndPicker(true)}> Change End @@ -245,7 +209,7 @@ const confirmDeleteAlarmSet = (id: string) => { is24Hour={false} onChange={(event, selectedDate) => { setShowStartPicker(Platform.OS === 'ios'); - if (selectedDate) setStartTime(selectedDate); + if (selectedDate) setStartTime(new DATE(selectedDate)); }} /> )} @@ -257,7 +221,7 @@ const confirmDeleteAlarmSet = (id: string) => { is24Hour={false} onChange={(event, selectedDate) => { setShowEndPicker(Platform.OS === 'ios'); - if (selectedDate) setEndTime(selectedDate); + if (selectedDate) setEndTime(new DATE(selectedDate)); }} /> )} From 4f6edb801b5fb90567ae04e67262122d92009284 Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Thu, 2 Apr 2026 00:32:09 -0700 Subject: [PATCH 04/27] update class w/ count for next interval, ... --- AlarmApp/alarm.tsx | 107 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/AlarmApp/alarm.tsx b/AlarmApp/alarm.tsx index d9af4c7..8ac526c 100644 --- a/AlarmApp/alarm.tsx +++ b/AlarmApp/alarm.tsx @@ -2,8 +2,6 @@ // ------------------------------------ // // IMPORTS // // ------------------------------------ // -//TODO: import Sound from "./Sound.tsx" - // ------------------------------------ // // TYPE DEFINITIONS // @@ -23,7 +21,7 @@ type MINUTE = int; export class DATE extends Date { date: Date; - constructor(date: Date){ + constructor(date: Date = new Date()){ super(); this.date = date; @@ -37,36 +35,107 @@ export class DATE extends Date { export interface ALARM_INTERFACE { - id: ID; - start: DATE; - end: DATE; - min_interval: MINUTE; - active: bool; + id: ID; + start: DATE; + end: DATE; + min_interval: MINUTE; + active: bool; + max_count: int; + current_count: int; + + //updateCurrentCount(): void; } export class ALARM implements ALARM_INTERFACE { // members - id: ID; - start: DATE; - end: DATE; - min_interval: MINUTE; - active: bool; - //TODO: add sound variable + id: ID; + start: DATE; + end: DATE; + min_interval: MINUTE; + active: bool; + max_count: int; + current_count: int; // functions constructor(times: Date[], interval: MINUTE, is_active: bool) { - this.id = generate_alarm_id(); // function creates new alarm id - this.start = new DATE(times[0]); - this.end = new DATE(times[1]); - this.min_interval = interval; - this.active = false; + this.id = generate_alarm_id(); // function creates new alarm id + this.start = new DATE(times[0]); + this.end = new DATE(times[1]); + this.min_interval = interval; + this.active = is_active; + this.max_count = generate_max_alarm_count(this.start, this.end, this.min_interval); + this.current_count = generate_current_alarm_count(this.start, this.end, this.min_interval, this.max_count); + } + + /* + updateCurrentCount(): void { + // increment current count, bound to 'max_count' + this.current_count++; + this.current_count %= this.max_count; } + */ } +// TODO: move generate functions back into class as 'init...' + + // ------------------------------------ // // FUNCTIONS // // ------------------------------------ // +export function generate_current_alarm_count(start: DATE, end: DATE, min_interval: int, max_count: int): int { + const now = new DATE(); + + const now_str = now.toDigitTime(); + const start_str = start.toDigitTime(); + const end_str = end.toDigitTime(); + + // alarm is BEFORE START alarm + // alarm is AFTER END alarm + if (now_str < start_str) return 0; + if (now_str > end_str) return 0; + + // alarm is EQUAL to START alarm + if (now_str == start_str) return 0; + + // calculate count BETWEEN start/end alarms + let count = 0; // -- to be returned below + + // cycle through each alarm interval + for(let i = 1; i < max_count; i++){ + // calculate hours/mins + const elapsed_mins = i * min_interval; + const t_mins = elapsed_mins % 60; + const t_hours = elapsed_mins / 60; + + // set current alarm interval + let current_time = start; + current_time.setHours(current_time.getHours() + t_hours); + current_time.setMinutes(current_time.getMinutes() + t_mins); + + const current_time_str = current_time.toDigitTime(); + + // if now is less than next interval, return + // else, increment counter + if (now_str < current_time_str) { + break; + } + count++; + } + + return count; +} +export function generate_max_alarm_count(start: DATE, end: DATE, min_interval: int): int { + const ms_in_min = 60_000; + const time_diff_min = (end.getTime() - start.getTime()) / ms_in_min; + const n_alarms = time_diff_min / min_interval; + + // return n+1 alarm for end time alarm + return n_alarms + 1; +} + + + let alarm_id = 0; export function generate_alarm_id(): string { alarm_id++; From 2ac678b077ff33bb78d18e54dd8cc767243bbd4b Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Sun, 12 Apr 2026 14:56:28 -0700 Subject: [PATCH 05/27] moved 'check_time' function from App to Time --- AlarmApp/App.tsx | 16 ---------------- AlarmApp/Time.tsx | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index 6b73e09..c1d6a4a 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -39,22 +39,6 @@ interface AlarmSet { -function check_time(current_time: Date, alarm_time: Date): boolean { - let CT: Date = current_time; // current time - let AT: Date = alarm_time; // alarm time - - // alarm should go off when both times match - if(CT.getHours() === AT.getHours()){ - if(CT.getMinutes() === AT.getMinutes()){ - return true; - } - } - - // the alarm should NOT go off when they are different - return false; -} - - export default function App() { const [alarms, setAlarms] = useState([]); const [startTime, setStartTime] = useState(new Date()); diff --git a/AlarmApp/Time.tsx b/AlarmApp/Time.tsx index 4b40d35..8370e33 100644 --- a/AlarmApp/Time.tsx +++ b/AlarmApp/Time.tsx @@ -19,4 +19,19 @@ export function get_interval_time(starting_time: Date, interval_time: number, to new_time.setMinutes(starting_time.getMinutes() + minutes_offset); return new_time; -} \ No newline at end of file +} + +export function check_time(current_time: Date, alarm_time: Date): boolean { + let CT: Date = current_time; // current time + let AT: Date = alarm_time; // alarm time + + // alarm should go off when both times match + if(CT.getHours() === AT.getHours()){ + if(CT.getMinutes() === AT.getMinutes()){ + return true; + } + } + + // the alarm should NOT go off when they are different + return false; +} From cbd11c9f038b403399053e072b761274f70efa42 Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Sun, 12 Apr 2026 16:33:41 -0700 Subject: [PATCH 06/27] implemented de/serialization for async database, wip --- AlarmApp/Alarm.tsx | 74 ++++++++++++++++++++++++++++++++-------------- AlarmApp/App.tsx | 59 ++++++++++++++---------------------- AlarmApp/Date.tsx | 7 +++-- 3 files changed, 78 insertions(+), 62 deletions(-) diff --git a/AlarmApp/Alarm.tsx b/AlarmApp/Alarm.tsx index af3c248..05dccf4 100644 --- a/AlarmApp/Alarm.tsx +++ b/AlarmApp/Alarm.tsx @@ -1,7 +1,9 @@ // ------------------------------------ // // IMPORTS // // ------------------------------------ // -import DATE from "./Date.tsx" +import { + DATE +} from "./Date.tsx" @@ -20,15 +22,23 @@ type MINUTE = int; // ------------------------------------ // // INTERFACE & CLASS // // ------------------------------------ // + +//! ================ !// +//! MUST IMPLEMENT !// +//! ================ !// +// TODO: toJSON / fromJSON (Serialize <--> Deserialize) +// TODO: save / load alarms + export interface ALARM_INTERFACE { id: ID; start: DATE; end: DATE; - next: DATE; min_interval: MINUTE; active: bool; - initNext() : DATE; + toJSON(): void; + fromJSON(obj: any) : ALARM; + copy(update: Partial): ALARM; } export class ALARM implements ALARM_INTERFACE { @@ -36,37 +46,55 @@ export class ALARM implements ALARM_INTERFACE { id: ID; start: DATE; end: DATE; - next: DATE; min_interval: MINUTE; active: bool; //TODO: add sound variable - // functions - constructor(times: Date[], interval: MINUTE, is_active: bool) { - this.id = generate_alarm_id(); // function creates new alarm id - this.start = new DATE(times[0]); - this.end = new DATE(times[1]); - this.next = this.initNext(); + constructor(start: Date, end: Date, interval: MINUTE, is_active: bool, id: ID = "") { + this.start = new DATE(start); + this.end = new DATE(end); + this.active = is_active; this.min_interval = interval; - this.active = false; - } - initNext() : DATE { - // get 'start' values - // create new instance of 'next' - let next_date = new DATE(this.start.date); - - // update and return 'next_date' - next_date.updateDigitTime(0, this.min_interval); - return next_date; + // overloaded constructor undefined members + this.id = id; + if(id == ""){ + this.id = generate_alarm_id(); + } } + toJSON() { + return { + id: this.id, + active: this.active, + start: this.start, + end: this.end, + min_interval: this.min_interval, + } + } + static fromJSON(obj: any) : ALARM { + return new ALARM( + new Date(obj.start.date ?? obj.start), + new Date(obj.end.date ?? obj.end), + obj.min_interval, + obj.active, + obj.id + ); + } + copy(update: Partial): ALARM { + const alarm = new ALARM( + this.start.date, + this.end.date, + this.min_interval, + this.active, + this.id + ); + + return Object.assign(alarm, update); + } } -// TODO: move generate functions back into class as 'init...' - - // ------------------------------------ // // FUNCTIONS // // ------------------------------------ // diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index c1d6a4a..287d8e2 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -18,34 +18,27 @@ import DateTimePicker from '@react-native-community/datetimepicker'; // local classes -import DATE from './Date.js'; +import { + DATE, + toDateDigitTime +} from './Date.tsx'; import { - ALARM -} from './Alarm.js'; + ALARM, + generate_alarm_id +} from './Alarm.tsx'; // local stylesheets import styles from "./styles.js"; -interface AlarmSet { - id: string; - start: string; - end: string; - interval: number; // minutes - count: number; // num of alarms - active: boolean; -} - - - export default function App() { - const [alarms, setAlarms] = useState([]); - const [startTime, setStartTime] = useState(new Date()); - const [endTime, setEndTime] = useState(() => { + const [alarms, setAlarms] = useState([]); + const [startTime, setStartTime] = useState(new DATE()); + const [endTime, setEndTime] = useState(() => { const d = new Date(); d.setHours(d.getHours() + 1); - return d; + return new DATE(d); }); const [intervalMinutes, setIntervalMinutes] = useState(10); const [showStartPicker, setShowStartPicker] = useState(false); @@ -58,8 +51,8 @@ export default function App() { // check every second useEffect(() => { const interval = setInterval(() => { - const now = new Date(); - const nowStr = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const now = new DATE(); + const nowStr = now.toDigitTime(); for(const set of alarms){ if(!set.active) continue; @@ -72,7 +65,7 @@ export default function App() { console.log("seconds == 0") // set end to string - const set_end_str = set.end.toDigitTime(); + const set_end_str = set.start.toDigitTime(); console.log(nowStr, set_end_str) @@ -86,7 +79,7 @@ export default function App() { //guards against overlapping alarms and re-firing within the same minute if (lastFiredRef.current[set.id] !== minuteKey) { lastFiredRef.current[set.id] = minuteKey; - Alert.alert("Alarm!!!!", `Alarm set ended at ${set.end}`); + Alert.alert("Alarm!!!!", `Alarm set ended at ${set.end.toDigitTime()}`); } } } @@ -119,7 +112,8 @@ export default function App() { try { // load stored alarms set const stored = await AsyncStorage.getItem('ALARMS'); if (stored !== null) { - setAlarms(JSON.parse(stored)); + //setAlarms(JSON.parse(stored)); + setAlarms(JSON.parse(stored).map(ALARM.fromJSON)); } } catch (e) { console.log('Failed to load alarms', e); @@ -135,26 +129,19 @@ export default function App() { const times = [startTime, endTime]; const interval = intervalMinutes; - const alarm = new ALARM(times, interval, true); + const alarm = new ALARM(startTime.date, endTime.date, interval, true); // {/*set alarm*/} //should not be in CreateIntervalAlarms - const newAlarmSet: AlarmSet = { - id: Date.now().toString(), - start: startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - end: endTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), - interval: intervalMinutes, - count, - active: true, - }; + //let newAlarmSet: ALARM = new ALARM(startTime, endTime, intervalMinutes, true); - setAlarms((prev) => [...prev, newAlarmSet]); - Alert.alert('Alarms Created!', `${count} alarms would be scheduled!`); + setAlarms((prev) => [...prev, alarm]); + //Alert.alert('Alarms Created!', `${count} alarms would be scheduled!`); }; -// Issues here - something with ID string/integers. + // Issues here - something with ID string/integers. const toggleAlarmSet = (id: string) => { setAlarms((prev) => - prev.map((a) => (a.id === id ? { ...a, active: !a.active } : a)) + prev.map((a): ALARM => (a.id === id ? a.copy({...a, active: !a.active }) : a)) ); }; diff --git a/AlarmApp/Date.tsx b/AlarmApp/Date.tsx index 57ff06d..33d87a0 100644 --- a/AlarmApp/Date.tsx +++ b/AlarmApp/Date.tsx @@ -19,7 +19,7 @@ const MINS_IN_HOUR = 60; // ------------------------------------ // // INTERFACE & CLASS // // ------------------------------------ // -class DATE extends Date { +export class DATE extends Date { // members date: Date; @@ -54,5 +54,6 @@ class DATE extends Date { } - -export default DATE; \ No newline at end of file +export function toDateDigitTime(date: Date) : string { + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); +} \ No newline at end of file From 176fd42fd5db4d241ecc45531596c5a50ca4b118 Mon Sep 17 00:00:00 2001 From: Tristen Hale Date: Mon, 20 Apr 2026 22:25:17 -0700 Subject: [PATCH 07/27] implemented sound, persistance now failing --- AlarmApp/Alarm.tsx | 23 ++++++++++++----------- AlarmApp/App.tsx | 28 +++++++++++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/AlarmApp/Alarm.tsx b/AlarmApp/Alarm.tsx index 05dccf4..8f118cf 100644 --- a/AlarmApp/Alarm.tsx +++ b/AlarmApp/Alarm.tsx @@ -4,7 +4,7 @@ import { DATE } from "./Date.tsx" - +import SOUND from "./Sound.tsx" // ------------------------------------ // @@ -22,22 +22,16 @@ type MINUTE = int; // ------------------------------------ // // INTERFACE & CLASS // // ------------------------------------ // - -//! ================ !// -//! MUST IMPLEMENT !// -//! ================ !// -// TODO: toJSON / fromJSON (Serialize <--> Deserialize) -// TODO: save / load alarms - export interface ALARM_INTERFACE { id: ID; start: DATE; end: DATE; min_interval: MINUTE; active: bool; + + sound: SOUND; toJSON(): void; - fromJSON(obj: any) : ALARM; copy(update: Partial): ALARM; } @@ -48,14 +42,18 @@ export class ALARM implements ALARM_INTERFACE { end: DATE; min_interval: MINUTE; active: bool; - //TODO: add sound variable - constructor(start: Date, end: Date, interval: MINUTE, is_active: bool, id: ID = "") { + sound: SOUND; + + constructor(start: Date, end: Date, interval: MINUTE, + sound_name: string = "mgs_codec.mp3", is_active: bool = true, id: ID = "") { this.start = new DATE(start); this.end = new DATE(end); this.active = is_active; this.min_interval = interval; + this.sound = new SOUND(sound_name); + // overloaded constructor undefined members this.id = id; if(id == ""){ @@ -70,6 +68,7 @@ export class ALARM implements ALARM_INTERFACE { start: this.start, end: this.end, min_interval: this.min_interval, + sound_name: this.sound.name } } static fromJSON(obj: any) : ALARM { @@ -77,6 +76,7 @@ export class ALARM implements ALARM_INTERFACE { new Date(obj.start.date ?? obj.start), new Date(obj.end.date ?? obj.end), obj.min_interval, + obj.sound_name, obj.active, obj.id ); @@ -86,6 +86,7 @@ export class ALARM implements ALARM_INTERFACE { this.start.date, this.end.date, this.min_interval, + this.sound.name, this.active, this.id ); diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index 287d8e2..8bd76e4 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -48,6 +48,8 @@ export default function App() { //guard against overlapping alarms const lastFiredRef = React.useRef>({}); + //const default_sound_name = "mgs_codec.mp3"; + // check every second useEffect(() => { const interval = setInterval(() => { @@ -62,13 +64,9 @@ export default function App() { //fire only once per minute to avoid overlapping alarms if(now.getSeconds() !== 0) continue; - console.log("seconds == 0") - // set end to string const set_end_str = set.start.toDigitTime(); - console.log(nowStr, set_end_str) - if(nowStr === set_end_str){ console.log("alarm ringing..."); @@ -78,8 +76,24 @@ export default function App() { //guards against overlapping alarms and re-firing within the same minute if (lastFiredRef.current[set.id] !== minuteKey) { - lastFiredRef.current[set.id] = minuteKey; - Alert.alert("Alarm!!!!", `Alarm set ended at ${set.end.toDigitTime()}`); + lastFiredRef.current[set.id] = minuteKey; + + // start alarm + set.sound.play(); + + Alert.alert( + "Alarm!!!!", `Alarm set ended at ${set.end.toDigitTime()}`, + [ + { + text: "OK", + onPress: () => { + // stop alarm + console.log("DEBUG] Alert - 'OK' pressed"); + set.sound.stop(); + } + }, + ], + ); } } } @@ -129,7 +143,7 @@ export default function App() { const times = [startTime, endTime]; const interval = intervalMinutes; - const alarm = new ALARM(startTime.date, endTime.date, interval, true); + const alarm = new ALARM(startTime.date, endTime.date, interval); // {/*set alarm*/} //should not be in CreateIntervalAlarms //let newAlarmSet: ALARM = new ALARM(startTime, endTime, intervalMinutes, true); From 188cac58bd3cb855166395ccdc997895e3fec019 Mon Sep 17 00:00:00 2001 From: Sarah Jennings Date: Mon, 27 Apr 2026 12:33:34 -0700 Subject: [PATCH 08/27] Fixing UI. Work in progress --- AlarmApp/App.tsx | 98 ++++++++++++++++++++++++++++++++++------------ AlarmApp/styles.js | 33 ++++++++++++++++ 2 files changed, 107 insertions(+), 24 deletions(-) diff --git a/AlarmApp/App.tsx b/AlarmApp/App.tsx index 62bb9b6..8ef8b4d 100644 --- a/AlarmApp/App.tsx +++ b/AlarmApp/App.tsx @@ -9,6 +9,7 @@ import { Switch, Alert, Platform, + Modal, } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; @@ -61,6 +62,7 @@ export default function App() { const [showEndPicker, setShowEndPicker] = useState(false); const [showIntervalPicker, setShowIntervalPicker] = useState(false); const [editingAlarmId, setEditingAlarmId] = useState(null); + const [showAlarmModal, setShowAlarmModal] = useState(false); //guard against overlapping alarms const lastFiredRef = React.useRef>({}); @@ -192,6 +194,8 @@ export default function App() { setStartTime(start); setEndTime(end); setIntervalMinutes(alarm.interval); + + setShowAlarmModal(true); }; const saveEditAlarmSet = () => { @@ -294,6 +298,64 @@ const confirmDeleteAlarmSet = (id: string) => { ListEmptyComponent={No alarms yet} /> + + + + + {editingAlarmId ? "Edit Alarm" : "Create Alarm"} + + + {/* start */} + Start Time + setShowStartPicker(true)}> + + {startTime.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit' + })} + + + + {/* end */} + End Time + setShowEndPicker(true)}> + + {endTime.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit' + })} + + + + {/* interval picker */} + Interval + setShowIntervalPicker(true)}> + + Every {intervalMinutes} minutes + + + +