Live video on the AT Protocol
at next 179 lines 4.2 kB view raw
1import { Text, zero } from "@streamplace/components"; 2import * as chrono from "chrono-node"; 3import { useEffect, useState } from "react"; 4import { View, useWindowDimensions } from "react-native"; 5 6interface CountdownProps { 7 from?: string; 8 to?: string; 9 small?: boolean; 10} 11 12interface LabelBoxProps { 13 children: React.ReactNode; 14 small?: boolean; 15} 16 17const LabelBox = ({ children, small }: LabelBoxProps) => { 18 return ( 19 <View 20 style={[ 21 { 22 borderColor: "white", 23 borderWidth: 0, 24 borderTopWidth: small ? 2 : 4, 25 borderStyle: "solid", 26 }, 27 ]} 28 > 29 <Text 30 style={[ 31 small ? { fontSize: 18 } : { fontSize: 24 }, 32 small ? { lineHeight: 24 } : { lineHeight: 28 }, 33 ]} 34 > 35 {children} 36 </Text> 37 </View> 38 ); 39}; 40 41export function Countdown({ from, to, small }: CountdownProps) { 42 const [now, setNow] = useState(Date.now()); 43 const [dest, setDest] = useState<number | null>(null); 44 const { width, height } = useWindowDimensions(); 45 46 useEffect(() => { 47 if (from) { 48 const fromDate = chrono.parseDate(from); 49 if (fromDate === null) { 50 throw new Error("could not parse from"); 51 } 52 setDest(fromDate.getTime()); 53 } else if (to) { 54 const toDate = chrono.parseDate(to); 55 if (toDate === null) { 56 throw new Error("could not parse to"); 57 } 58 setDest(toDate.getTime()); 59 } else { 60 throw new Error("must provide either from or to"); 61 } 62 }, [from, to]); 63 64 useEffect(() => { 65 const tick = () => { 66 if (!running) { 67 return; 68 } 69 requestAnimationFrame(tick); 70 setNow(Date.now()); 71 }; 72 let running = true; 73 tick(); 74 return () => { 75 running = false; 76 }; 77 }, []); 78 79 if (dest === null) { 80 return <View />; 81 } 82 83 let diff = Math.abs(dest - now); 84 if (to && now > dest) { 85 diff = 0; 86 } else if (from && now < dest) { 87 diff = 0; 88 } 89 small = small ?? width <= 600; 90 const [years, days, hrs, min, sec, ms] = toLabels(diff); 91 92 const unitStyle = [ 93 zero.mx[small ? 2 : 4], 94 zero.flex.values[0], 95 { flexDirection: "column" }, 96 ]; 97 98 const timeTextStyle = [ 99 { fontFamily: "monospace" }, 100 small ? { fontSize: 18 } : { fontSize: 128 }, 101 small ? { lineHeight: 24 } : { lineHeight: 40 }, 102 ]; 103 104 return ( 105 <View 106 style={[ 107 { flexDirection: "row" }, 108 { alignSelf: small ? "auto" : "center" }, 109 ]} 110 > 111 <View style={[{ flexDirection: "row" }, { justifyContent: "flex-end" }]}> 112 <View style={unitStyle}> 113 <Text style={timeTextStyle}>{years}</Text> 114 <LabelBox small={small}>YEARS</LabelBox> 115 </View> 116 <View style={unitStyle}> 117 <Text style={timeTextStyle}>{days}</Text> 118 <LabelBox small={small}>DAYS</LabelBox> 119 </View> 120 <View style={unitStyle}> 121 <Text style={timeTextStyle}>{hrs}</Text> 122 <LabelBox small={small}>HRS</LabelBox> 123 </View> 124 </View> 125 <View style={[{ flexDirection: "row" }, { justifyContent: "flex-end" }]}> 126 <View style={unitStyle}> 127 <Text style={timeTextStyle}>{min}</Text> 128 <LabelBox small={small}>MIN</LabelBox> 129 </View> 130 <View style={unitStyle}> 131 <Text style={timeTextStyle}>{sec}</Text> 132 <LabelBox small={small}>SEC</LabelBox> 133 </View> 134 <View style={unitStyle}> 135 <Text style={timeTextStyle}>{ms}</Text> 136 <LabelBox small={small}>MS</LabelBox> 137 </View> 138 </View> 139 </View> 140 ); 141} 142 143const toLabels = ( 144 now: number, 145): [string, string, string, string, string, string] => { 146 const ms = now % 1000; 147 now = Math.floor(now / 1000); 148 149 const sec = now % 60; 150 now = Math.floor(now / 60); 151 152 const min = now % 60; 153 now = Math.floor(now / 60); 154 155 const hrs = now % 24; 156 now = Math.floor(now / 24); 157 158 const days = now % 365; 159 now = Math.floor(now / 365); 160 161 const years = now; 162 163 return [ 164 pad(years, 4), 165 pad(days, 3), 166 pad(hrs, 2), 167 pad(min, 2), 168 pad(sec, 2), 169 pad(ms, 3), 170 ]; 171}; 172 173const pad = (num: number, n: number): string => { 174 let str = `${num}`; 175 while (str.length < n) { 176 str = "0" + str; 177 } 178 return str; 179};