Skip to content
Open
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
120 changes: 96 additions & 24 deletions AlarmApp/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Switch,
Alert,
Platform,
Modal,
} from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';

Expand Down Expand Up @@ -61,6 +62,8 @@ export default function App() {
const [showEndPicker, setShowEndPicker] = useState<boolean>(false);
const [showIntervalPicker, setShowIntervalPicker] = useState<boolean>(false);
const [editingAlarmId, setEditingAlarmId] = useState<string | null>(null);
const [showAlarmModal, setShowAlarmModal] = useState(false);
const [intervalDropdownOpen, setIntervalDropdownOpen] = useState(false);

//guard against overlapping alarms
const lastFiredRef = React.useRef<Record<string, string>>({});
Expand Down Expand Up @@ -192,6 +195,8 @@ export default function App() {
setStartTime(start);
setEndTime(end);
setIntervalMinutes(alarm.interval);

setShowAlarmModal(true);
};

const saveEditAlarmSet = () => {
Expand Down Expand Up @@ -274,6 +279,8 @@ const confirmDeleteAlarmSet = (id: string) => {
<Switch
value={item.active}
onValueChange={() => toggleAlarmSet(item.id)}
trackColor={{ false: 'grey', true: '#d6ecff' }}
thumbColor={item.active ? '#2196f3' : '#d6ecff'}
/>
{/* summarized alarm text */}
<Text style={styles.alarmText}>
Expand All @@ -294,6 +301,83 @@ const confirmDeleteAlarmSet = (id: string) => {
ListEmptyComponent={<Text style={styles.emptyText}>No alarms yet</Text>}
/>

<Modal
visible = {showAlarmModal}
animationType = "slide"
transparent = {true}
>
<View style = {styles.modalOverlay}>
<View style = {styles.modalBox}>
<Text style={styles.title}>
{editingAlarmId ? "Edit Alarm" : "Create Alarm"}
</Text>

{/* start */}
<Text style = {styles.summaryLabel}>Start Time</Text>
<TouchableOpacity onPress = {() => setShowStartPicker(true)}>
<Text style = {styles.timeText}>
{startTime.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})}
</Text>
</TouchableOpacity>

{/* end */}
<Text style = {styles.summaryLabel}>End Time</Text>
<TouchableOpacity onPress = {() => setShowEndPicker(true)}>
<Text style = {styles.timeText}>
{endTime.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})}
</Text>
</TouchableOpacity>

{/* interval picker */}
<Text style = {styles.summaryLabel}>Interval</Text>
<TouchableOpacity
onPress = {() => setIntervalDropdownOpen(!intervalDropdownOpen)}
style = {styles.dropdownButton}
>
<Text style = {styles.timeText}>
Every {intervalMinutes} minutes
</Text>
</TouchableOpacity>

{intervalDropdownOpen && (
<View style = {styles.dropdownBox}>
{intervalOptions.map((min) => (
<TouchableOpacity
key = {min}
onPress = {() => {
setIntervalMinutes(min);
setIntervalDropdownOpen(false);
}}
style = {styles.dropdownItem}
>
<Text>{min} minutes</Text>
</TouchableOpacity>
))}
</View>
)}
<Button
title = {editingAlarmId ? "Save Edit" : "Create"}
onPress={() => {
editingAlarmId ? saveEditAlarmSet() : CreateIntervalAlarms();
setShowAlarmModal(false);
}}
/>

<Button
title = "Cancel"
color = "red"
onPress={() => setShowAlarmModal(false)}
/>
</View>
</View>
</Modal>

{/*Start time picker*/}
<View style={styles.summary}>
<Text style={styles.summaryLabel}>Start Time</Text>
Expand All @@ -303,30 +387,6 @@ const confirmDeleteAlarmSet = (id: string) => {
</Text>
</TouchableOpacity>

{/* end time picker */}
<Text style={styles.summaryLabel}>End Time</Text>
<TouchableOpacity onPress={() => setShowEndPicker(true)}>
<Text style={styles.timeText}>
{endTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</Text>
</TouchableOpacity>

{/* interval picker */}
<Text style={styles.summaryLabel}>Interval</Text>
<TouchableOpacity
onPress={() => setShowIntervalPicker(true)}
activeOpacity={0.7}
>
<Text style={styles.timeText}>Every {intervalMinutes} minutes</Text>
</TouchableOpacity>
</View>

<Button
title={editingAlarmId ? "Save Edit" : "Create Alarm Set"}
onPress={editingAlarmId ? saveEditAlarmSet : CreateIntervalAlarms}
color="#4CAF50"
/>

{/* Time Pickers */}
{showStartPicker && (
<DateTimePicker
Expand Down Expand Up @@ -374,6 +434,18 @@ const confirmDeleteAlarmSet = (id: string) => {
</TouchableOpacity>
</View>
)}

{/* + button */}
<TouchableOpacity
style={styles.fab}
onPress={() => {
setEditingAlarmId(null);
setShowAlarmModal(true);
}}
>
<Text style={styles.fabText}>+</Text>
</TouchableOpacity>
</View>
</View>
);
}
76 changes: 65 additions & 11 deletions AlarmApp/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#fff'
backgroundColor: '#FAF8F2' //soft paper white
},

title: {
Expand All @@ -17,16 +17,17 @@ const styles = StyleSheet.create({
textAlign: 'center'
},

//alarm batch/set
alarmItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 14,
borderBottomWidth: 1,
borderColor: '#ddd',
backgroundColor: '#fff',
backgroundColor: 'white',
},

alarmText: {
alarmText: {
marginLeft: 12,
fontSize: 16,
flex: 1,
Expand All @@ -52,17 +53,17 @@ const styles = StyleSheet.create({
emptyText: {
textAlign: 'center',
marginTop: 40,
color: '#888',
color: '#888',
fontSize: 16
},

summary: {
marginVertical: 24,
padding: 16,
backgroundColor: '#f8f9fa',
backgroundColor: '#faf8f2', //soft paper white
borderRadius: 12,
borderWidth: 1,
borderColor: '#e0e0e0',
borderColor: '#2196f3',
},

summaryLabel: {
Expand All @@ -79,31 +80,33 @@ const styles = StyleSheet.create({
paddingVertical: 10,
paddingHorizontal: 14,
marginVertical: 4,
backgroundColor: '#fff',
backgroundColor: 'white', //pale blue
borderRadius: 10,
borderWidth: 1,
borderColor: '#e0e0e0',

// slight shadow to make touchable noticable
shadowColor: '#000',
shadowColor: 'black',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.12,
shadowRadius: 3,
elevation: 2,
},

clickable: {
color: '#2196F3',
marginVertical: 4,
color: '#2196f3', //Recycling-bin blue
marginVertical: 4,
fontWeight: '500'
},

/* old interval picker, not used
intervalPicker: {
marginTop: 20,
padding: 20,
backgroundColor: '#f0f0f0',
backgroundColor: 'red',
borderRadius: 12,
},
*/

intervalOption: {
paddingVertical: 14,
Expand All @@ -119,6 +122,57 @@ const styles = StyleSheet.create({
fontSize: 18,
fontWeight: '500',
},

fab: {
position: 'absolute',
bottom: 80,
right: -2,
backgroundColor: '#2196f3', //Recycling bin blue
width: 60,
height: 60,
borderRadius: 30,
justifyContent: 'center',
alignItems: 'center',
elevation: 5,
},

fabText: {
color: 'white',
fontSize: 32,
fontWeight: 'bold',
},

modalOverlay: {
flex: 1,
backgroundColor: '#d6ecff', //pale blue
justifyContent: 'center',
alignItems: 'center',
},

modalBox: {
width: '85%',
backgroundColor: '#faf8f2', //soft paper white
borderRadius: 15,
padding: 20,
},

dropdownBox: {
backgroundColor: 'white',
borderWidth: 1,
borderColor: 'black',
borderRadius: 8,
marginTop: 5,
},

dropdownItem: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: 'white'
},

dropdownButton: {
paddingVertical: 10,
},
});


Expand Down
6 changes: 6 additions & 0 deletions node_modules/.package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}