|
| 1 | +import Exponent from 'exponent'; |
| 2 | +import React, { Component } from 'react'; |
| 3 | +import { StyleSheet, Text, View } from 'react-native'; |
| 4 | +import Svg, { G, Path } from 'react-native-svg'; |
| 5 | + |
| 6 | +import CircularSlider from 'react-native-circular-slider'; |
| 7 | +import TimerText from './components/TimerText'; |
| 8 | + |
| 9 | +const WAKE_ICON = ( |
| 10 | + <G> |
| 11 | + <Path d="M2,12.9h1.7h3h2.7h3H14c0.4,0,0.7-0.3,0.7-0.7c0-0.4-0.3-0.7-0.7-0.7c-0.9,0-1.7-0.7-1.7-1.7v-4 |
| 12 | + c0-2.1-1.5-3.8-3.4-4.2C9,1.6,9,1.4,9,1.3c0-0.5-0.4-1-1-1c-0.5,0-1,0.4-1,1c0,0.2,0,0.3,0.1,0.4c-2,0.4-3.4,2.1-3.4,4.2v4 |
| 13 | + c0,0.9-0.7,1.7-1.7,1.7c-0.4,0-0.7,0.3-0.7,0.7C1.3,12.6,1.6,12.9,2,12.9z"/> |
| 14 | + <Path d="M8,15.7c1.1,0,2.1-0.9,2.1-2.1H5.9C5.9,14.8,6.9,15.7,8,15.7z"/> |
| 15 | + </G> |
| 16 | +); |
| 17 | + |
| 18 | +const BEDTIME_ICON = ( |
| 19 | + <G> |
| 20 | + <Path d="M11.7,10.5c-3.6,0-6.4-2.9-6.4-6.4c0-0.7,0.1-1.4,0.4-2.1C3.1,2.9,1.2,5.3,1.2,8.1c0,3.6,2.9,6.4,6.4,6.4 |
| 21 | + c2.8,0,5.2-1.8,6.1-4.4C13.1,10.4,12.4,10.5,11.7,10.5z"/> |
| 22 | + <Path d="M8,7.6l2-2.5H8V4.4H11v0.6L9,7.6h2v0.7H8V7.6z"/> |
| 23 | + <Path d="M11.7,5.4l1.5-1.9h-1.4V3h2.2v0.5l-1.5,1.9h1.5v0.5h-2.2V5.4z"/> |
| 24 | + <Path d="M9.4,3l1.1-1.4h-1V1.3H11v0.4L9.9,3H11v0.4H9.4V3z"/> |
| 25 | + </G> |
| 26 | +); |
| 27 | + |
| 28 | + |
| 29 | +function calculateMinutesFromAngle(angle) { |
| 30 | + return Math.round(angle / (2 * Math.PI / (12 * 12))) * 5; |
| 31 | +} |
| 32 | + |
| 33 | +function calculateTimeFromAngle(angle) { |
| 34 | + const minutes = calculateMinutesFromAngle(angle); |
| 35 | + const h = Math.floor(minutes / 60); |
| 36 | + const m = minutes - h * 60; |
| 37 | + |
| 38 | + return { h, m }; |
| 39 | +} |
| 40 | + |
| 41 | +function roundAngleToFives(angle) { |
| 42 | + const fiveMinuteAngle = 2 * Math.PI / 144; |
| 43 | + |
| 44 | + return Math.round(angle / fiveMinuteAngle) * fiveMinuteAngle; |
| 45 | +} |
| 46 | + |
| 47 | +function padMinutes(min) { |
| 48 | + if (`${min}`.length < 2) { |
| 49 | + return `0${min}`; |
| 50 | + } |
| 51 | + |
| 52 | + return min; |
| 53 | +} |
| 54 | + |
| 55 | +export default class Bedtime extends Component { |
| 56 | + |
| 57 | + state = { |
| 58 | + startAngle: Math.PI * 10/6, |
| 59 | + angleLength: Math.PI * 7/6, |
| 60 | + } |
| 61 | + |
| 62 | + onTimeUpdate = (fromTimeInMinutes, minutesLong) => { |
| 63 | + this.setState({ minutesLong }); |
| 64 | + } |
| 65 | + |
| 66 | + onUpdate = ({ startAngle, angleLength }) => { |
| 67 | + this.setState({ |
| 68 | + startAngle: roundAngleToFives(startAngle), |
| 69 | + angleLength: roundAngleToFives(angleLength) |
| 70 | + }); |
| 71 | + } |
| 72 | + |
| 73 | + render() { |
| 74 | + const { startAngle, angleLength } = this.state; |
| 75 | + const bedtime = calculateTimeFromAngle(startAngle); |
| 76 | + const waketime = calculateTimeFromAngle((startAngle + angleLength) % (2 * Math.PI)); |
| 77 | + |
| 78 | + return ( |
| 79 | + <View style={styles.container}> |
| 80 | + <View style={styles.timeContainer}> |
| 81 | + <View style={styles.time}> |
| 82 | + <View style={styles.timeHeader}> |
| 83 | + <Svg height={16} width={16}> |
| 84 | + <G fill="#ff9800">{BEDTIME_ICON}</G> |
| 85 | + </Svg> |
| 86 | + <Text style={styles.bedtimeText}>Bedtime</Text> |
| 87 | + </View> |
| 88 | + <Text style={styles.timeValue}>{bedtime.h}:{padMinutes(bedtime.m)}</Text> |
| 89 | + </View> |
| 90 | + <View style={styles.time}> |
| 91 | + <View style={styles.timeHeader}> |
| 92 | + <Svg height={16} width={16}> |
| 93 | + <G fill="#ffcf00">{WAKE_ICON}</G> |
| 94 | + </Svg> |
| 95 | + <Text style={styles.wakeText}>Wake</Text> |
| 96 | + </View> |
| 97 | + <Text style={styles.timeValue}>{waketime.h}:{padMinutes(waketime.m)}</Text> |
| 98 | + </View> |
| 99 | + </View> |
| 100 | + <View> |
| 101 | + <TimerText |
| 102 | + style={styles.sleepTimeContainer} |
| 103 | + minutesLong={calculateMinutesFromAngle(angleLength)} |
| 104 | + /> |
| 105 | + <CircularSlider |
| 106 | + startAngle={startAngle} |
| 107 | + angleLength={angleLength} |
| 108 | + onUpdate={this.onUpdate} |
| 109 | + segments={5} |
| 110 | + strokeWidth={40} |
| 111 | + radius={145} |
| 112 | + gradientColorFrom="#ff9800" |
| 113 | + gradientColorTo="#ffcf00" |
| 114 | + showClockFace |
| 115 | + clockFaceColor="#9d9d9d" |
| 116 | + bgCircleColor="#171717" |
| 117 | + stopIcon={<G scale="1.1" transform={{ translate: "-8, -8" }}>{WAKE_ICON}</G>} |
| 118 | + startIcon={<G scale="1.1" transform={{ translate: "-8, -8" }}>{BEDTIME_ICON}</G>} |
| 119 | + /> |
| 120 | + </View> |
| 121 | + </View> |
| 122 | + ); |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +const styles = StyleSheet.create({ |
| 127 | + container: { |
| 128 | + flex: 1, |
| 129 | + alignItems: 'center', |
| 130 | + justifyContent: 'center', |
| 131 | + backgroundColor: '#0D0D0D', |
| 132 | + }, |
| 133 | + bedtimeText: { |
| 134 | + color: '#ff9800', |
| 135 | + marginLeft: 3, |
| 136 | + fontSize: 16, |
| 137 | + }, |
| 138 | + wakeText: { |
| 139 | + color: '#ffcf00', |
| 140 | + marginLeft: 3, |
| 141 | + fontSize: 16, |
| 142 | + }, |
| 143 | + timeContainer: { |
| 144 | + flexDirection: 'row', |
| 145 | + justifyContent: 'space-between', |
| 146 | + marginBottom: 20, |
| 147 | + }, |
| 148 | + time: { |
| 149 | + alignItems: 'center', |
| 150 | + flex: 1, |
| 151 | + }, |
| 152 | + timeHeader: { |
| 153 | + flexDirection: 'row', |
| 154 | + alignItems: 'center', |
| 155 | + }, |
| 156 | + timeValue: { |
| 157 | + color: 'white', |
| 158 | + fontSize: 35, |
| 159 | + fontWeight: "300", |
| 160 | + }, |
| 161 | + sleepTimeContainer: { |
| 162 | + flex: 1, |
| 163 | + justifyContent: 'center', |
| 164 | + position: 'absolute', |
| 165 | + top: 0, |
| 166 | + bottom: 0, |
| 167 | + left: 0, |
| 168 | + right: 0 |
| 169 | + } |
| 170 | +}); |
| 171 | + |
| 172 | +Exponent.registerRootComponent(Bedtime); |
0 commit comments