WeekCalendar.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import { View, ScrollView, Text, Swiper, SwiperItem } from "@tarojs/components";
  2. import './WeekCalendar.scss'
  3. import { useEffect, useState, useMemo, memo, useRef } from "react";
  4. import { ColorType } from "@/context/themes/color";
  5. import WeekCalendarItem from "./WeekCalendarItem";
  6. import { rpxToPx } from "@/utils/tools";
  7. import { TimeFormatter } from "@/utils/time_format";
  8. import { clockSummaryStats, eatWakes } from "@/services/trackTimeDuration";
  9. import { useTranslation } from "react-i18next";
  10. import { IconBigArrow } from "@/components/basic/Icons";
  11. import dayjs from "dayjs";
  12. let pageIndex = 0
  13. let lastFastValue = 0;
  14. let lastSleepValue = 0;
  15. let lastOffset;
  16. let LinearGradient;
  17. if (process.env.TARO_ENV == 'rn') {
  18. LinearGradient = require('react-native-linear-gradient').default
  19. }
  20. const WeekCalendar = memo((props: { isFastSleep: boolean }) => {
  21. const [calendars, setCalendars] = useState<any>([])
  22. const [current, setCurrent] = useState(0)
  23. const [summary, setSummary] = useState<any>(null)
  24. const [calendarData, setCalendarData] = useState<any>(null)
  25. const [minTime, setMinTime] = useState(0)
  26. const [left, setLeft] = useState(0)
  27. const [isLoading, setIsLoading] = useState(false)
  28. const { t } = useTranslation()
  29. const ref = useRef(null)
  30. const pageSize = 40
  31. useEffect(() => {
  32. // pageIndex = -1
  33. getRecords()
  34. if (props.isFastSleep) {
  35. global.refrehWeekly = () => {
  36. console.log('正在刷新weekly', isLoading)
  37. // getRecords()
  38. if (isLoading) return
  39. pageIndex = 0;
  40. setIsLoading(true)
  41. var timestamp = TimeFormatter.getMondayTimestamp()
  42. var now = new Date()
  43. if (now.getDay() == 0 && now.getHours() >= 0 && now.getHours() < 12) {
  44. timestamp -= 24 * 3600 * 1000 * 7
  45. }
  46. var list: any = []
  47. var offset = 12 * 3600 * 1000 + pageIndex * pageSize * 24 * 3600 * 1000
  48. if (!props.isFastSleep) {
  49. offset = pageIndex * pageSize * 24 * 3600 * 1000
  50. }
  51. for (var i = 0; i < pageSize; i++) {
  52. list.push(`${i + pageIndex * pageSize},${timestamp - 7 * 24 * 3600 * 1000 * i - offset},${timestamp - 7 * 24 * 3600 * 1000 * i + 7 * 24 * 3600 * 1000 - offset}`)
  53. }
  54. if (props.isFastSleep) {
  55. clockSummaryStats({ times: list.join(';') }).then(res => {
  56. var list = (res as any).data.reverse()
  57. if (pageIndex == 0) {
  58. setCalendars(list)
  59. setSummary(list[current].summary_stats ? list[current].summary_stats : null)
  60. setCalendarData(list[current])
  61. setIsLoading(false)
  62. }
  63. })
  64. }
  65. else {
  66. eatWakes({ times: list.join(';') }).then(res => {
  67. var list = (res as any).data.reverse()
  68. if (pageIndex == 0) {
  69. setCalendars(list)
  70. setSummary(list[current].summary_stats ? list[current].summary_stats : null)
  71. setCalendarData(list[current])
  72. setIsLoading(false)
  73. }
  74. })
  75. }
  76. }
  77. }
  78. }, [])
  79. function getRecords() {
  80. if (isLoading) return
  81. pageIndex = 0;
  82. setIsLoading(true)
  83. var timestamp = TimeFormatter.getMondayTimestamp()
  84. var now = new Date()
  85. if (now.getDay() == 0 && now.getHours() >= 0 && now.getHours() < 12) {
  86. timestamp -= 24 * 3600 * 1000 * 7
  87. }
  88. var list: any = []
  89. var offset = 12 * 3600 * 1000 + pageIndex * pageSize * 24 * 3600 * 1000
  90. if (!props.isFastSleep) {
  91. timestamp = TimeFormatter.getSundayTimestamp()
  92. offset = pageIndex * pageSize * 24 * 3600 * 1000
  93. }
  94. for (var i = 0; i < pageSize; i++) {
  95. list.push(`${i + pageIndex * pageSize},${timestamp - 7 * 24 * 3600 * 1000 * i - offset},${timestamp - 7 * 24 * 3600 * 1000 * i + 7 * 24 * 3600 * 1000 - offset}`)
  96. }
  97. if (props.isFastSleep) {
  98. clockSummaryStats({ times: list.join(';') }).then(res => {
  99. var list = (res as any).data.reverse()
  100. if (list.length > 1) {
  101. lastFastValue = list[list.length - 2].summary_stats.fast.avg
  102. lastSleepValue = list[list.length - 2].summary_stats.sleep.avg
  103. }
  104. if (pageIndex == 0) {
  105. setCalendars(list)
  106. setCurrent(list.length - 1)
  107. setSummary(list[list.length - 1].summary_stats ? list[list.length - 1].summary_stats : null)
  108. setCalendarData(list[list.length - 1])
  109. setMinTime((res as any).extra.real_start_time_min)
  110. setLeft((list.length - 1) * parseInt(rpxToPx(658) + ''))
  111. setIsLoading(false);
  112. if (ref.current) {
  113. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  114. setTimeout(() => {
  115. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  116. }, 500)
  117. }
  118. }
  119. else {
  120. }
  121. })
  122. }
  123. else {
  124. eatWakes({ times: list.join(';') }).then(res => {
  125. var list = (res as any).data.reverse()
  126. if (list.length > 1) {
  127. lastFastValue = list[list.length - 2].summary_stats.eat.avg
  128. lastSleepValue = list[list.length - 2].summary_stats.wake.avg
  129. }
  130. if (pageIndex == 0) {
  131. setCalendars(list)
  132. setCurrent(list.length - 1)
  133. setSummary(list[list.length - 1].summary_stats ? list[list.length - 1].summary_stats : null)
  134. setCalendarData(list[list.length - 1])
  135. if ((res as any).extra) {
  136. setMinTime((res as any).extra.real_start_time_min)
  137. }
  138. setLeft((list.length - 1) * parseInt(rpxToPx(658) + ''))
  139. setIsLoading(false);
  140. if (ref.current) {
  141. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  142. setTimeout(() => {
  143. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  144. }, 500)
  145. }
  146. }
  147. else {
  148. }
  149. })
  150. }
  151. // clockSummaryStats({start:new Date().getTime()-7*24*3600*1000,end:new Date().getTime()}).then(res => { })
  152. }
  153. function dragStart(e) {
  154. // fingerDrag = true
  155. }
  156. function dragEnd(e) {
  157. // fingerDrag = false
  158. }
  159. function onScroll(e) {
  160. // if (!fingerDrag && process.env.TARO_ENV == 'weapp') return
  161. if (lastOffset && Math.abs(lastOffset - e.detail.scrollLeft) > 300) {
  162. setLeft(lastOffset)
  163. return
  164. }
  165. // isScrolling = true
  166. var x = e.detail.scrollLeft + 5
  167. lastOffset = e.detail.scrollLeft
  168. // var page = parseInt(x / parseInt(rpxToPx(658) + '') + '')
  169. var page = Math.round(x / parseInt(rpxToPx(658) + ''))
  170. console.log(page, x)
  171. if (current != page && page < calendars.length && page >= 0) {
  172. lastFastValue = calendars[current].summary_stats.fast.avg
  173. lastSleepValue = calendars[current].summary_stats.sleep.avg
  174. setCurrent(page)
  175. setSummary(calendars[page].summary_stats ? calendars[page].summary_stats : null)
  176. setCalendarData(calendars[page])
  177. // if (page <= 0) {
  178. // getRecords();
  179. // }
  180. }
  181. // if (scrollTimer)
  182. // clearTimeout(scrollTimer);
  183. // scrollTimer = setTimeout(() => {
  184. // isScrolling = false
  185. // fingerDrag = false;
  186. // }, 300) as any
  187. }
  188. function onChange(e) {
  189. var page = e.detail.current
  190. lastFastValue = props.isFastSleep ? calendars[current].summary_stats.fast.avg : calendars[current].summary_stats.eat.avg
  191. lastSleepValue = props.isFastSleep ? calendars[current].summary_stats.sleep.avg : calendars[current].summary_stats.wake.avg
  192. setCurrent(page)
  193. setSummary(calendars[page].summary_stats ? calendars[page].summary_stats : null)
  194. setCalendarData(calendars[page])
  195. }
  196. function getDate(timestamp) {
  197. var date = new Date(timestamp)
  198. if (global.language == 'en') {
  199. return dayjs(timestamp).format('MMM D')
  200. }
  201. return `${date.getMonth() + 1}月${date.getDate()}日`
  202. }
  203. function getWeekDuration() {
  204. if (global.language == 'en') {
  205. if (!props.isFastSleep) {
  206. return `${t('feature.common.day_of_week_full.sun')}, ${getDate(calendarData.start)} - ${t('feature.common.day_of_week_full.sat')}, ${getDate(calendarData.end - 1)}`
  207. }
  208. return `${t('feature.common.day_of_week_full.sun')} ${t('feature.common.time_desc.afternoon')}, ${getDate(calendarData.start)} - ${t('feature.common.day_of_week_full.sun')} ${t('feature.common.time_desc.morning')}, ${getDate(calendarData.end)}`
  209. }
  210. if (!props.isFastSleep) {
  211. return `${getDate(calendarData.start)} ${t('feature.common.day_of_week_full.sun')} - ${getDate(calendarData.end - 1)} ${t('feature.common.day_of_week_full.sat')}`
  212. }
  213. return `${getDate(calendarData.start)} ${t('feature.common.day_of_week_full.sun')}${t('feature.common.time_desc.afternoon')} - ${getDate(calendarData.end)} ${t('feature.common.day_of_week_full.sun')}${t('feature.common.time_desc.morning')}`
  214. }
  215. function getWeekIndex() {
  216. // style={{ opacity: current == calendars.length - 1 ? 1 : 0 }}
  217. if (current == calendars.length - 1) {
  218. return t('feature.common.week_desc.current_week')
  219. }
  220. else if (current == calendars.length - 2) {
  221. return t('feature.common.week_desc.last_week')
  222. }
  223. else {
  224. return t('feature.common.week_desc.weeks_ago', { index: calendars.length - current - 1 })
  225. }
  226. }
  227. function showWeeklyItem(index) {
  228. if (process.env.TARO_ENV == 'weapp') {
  229. if (index >= current - 1 && index <= current + 1) {
  230. return true
  231. }
  232. return false
  233. }
  234. if (index >= current - 2 && index <= current + 2) {
  235. return true
  236. }
  237. return false
  238. }
  239. if (!calendarData)
  240. return <View style={{ height: parseInt(rpxToPx(520) + '') + rpxToPx(16) + rpxToPx(32) + rpxToPx(20) + rpxToPx(72) }} />
  241. return <View style={{ display: 'flex', flexDirection: 'column' }}>
  242. <View className="calendar_summary_top">
  243. <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
  244. {
  245. !summary || (props.isFastSleep ? summary.fast.avg == 0 : summary.eat.avg == 0) ? <View className="calendar-empty-bg" style={{ borderColor: props.isFastSleep ? ColorType.fast : ColorType.food }}>
  246. <View className="calendar-empty-line" style={{ backgroundColor: props.isFastSleep ? ColorType.fast : ColorType.food }} />
  247. </View> :
  248. <View className={(props.isFastSleep ? lastFastValue > summary.fast.avg : lastFastValue > summary.eat.avg) ? 'calendar-arrow-bg-down' : 'calendar-arrow-bg'} style={{ backgroundColor: props.isFastSleep ? ColorType.fast : ColorType.food }}>
  249. <IconBigArrow color="#fff" width={rpxToPx(32)} />
  250. </View>
  251. }
  252. <View className="calendar_summary_item">
  253. <Text className="calendar_summary_title">{props.isFastSleep ? t('feature.track_time_duration.weekly.fast_average') : t('feature.track_time_duration.weekly.eat_average')}</Text>
  254. <Text className="calendar_summary_value" style={{ color: props.isFastSleep ? ColorType.fast : ColorType.food }}>{!summary || (props.isFastSleep ? summary.fast.avg == 0 : summary.eat.avg == 0) ? t('feature.track_time_duration.record_fast_sleep.none') : TimeFormatter.calculateTimeDifference(new Date().getTime(), new Date().getTime() + (props.isFastSleep ? summary.fast.avg : summary.eat.avg))}</Text>
  255. </View>
  256. </View>
  257. <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
  258. {
  259. !summary || (props.isFastSleep ? summary.sleep.avg == 0 : summary.wake.avg == 0) ? <View className="calendar-empty-bg" style={{ borderColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>
  260. <View className="calendar-empty-line" style={{ backgroundColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }} />
  261. </View> :
  262. <View className={lastSleepValue > (props.isFastSleep ? summary.sleep.avg : summary.wake.avg) ? 'calendar-arrow-bg-down' : 'calendar-arrow-bg'} style={{ backgroundColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>
  263. <IconBigArrow color="#fff" width={rpxToPx(32)} />
  264. </View>
  265. }
  266. <View className="calendar_summary_item">
  267. <Text className="calendar_summary_title">{props.isFastSleep ? t('feature.track_time_duration.weekly.sleep_average') : t('feature.track_time_duration.weekly.wake_average')}</Text>
  268. <Text className="calendar_summary_value" style={{ color: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>{!summary || (props.isFastSleep ? summary.sleep.avg == 0 : summary.wake.avg == 0) ? t('feature.track_time_duration.record_fast_sleep.none') : TimeFormatter.calculateTimeDifference(new Date().getTime(), new Date().getTime() + (props.isFastSleep ? summary.sleep.avg : summary.wake.avg))}</Text>
  269. </View>
  270. </View>
  271. </View>
  272. <View style={{ display: 'flex', flexDirection: 'row', marginBottom: rpxToPx(16), alignItems: 'center' }}>
  273. <View className="calendar_this_week" >{getWeekIndex()}</View>
  274. <Text className="calendar_summary_desc">{getWeekDuration()}</Text>
  275. </View>
  276. <View className="chart_bg">
  277. <View className="chart_bg">
  278. {
  279. process.env.TARO_ENV == 'weapp' ? <View className={props.isFastSleep?"chart_content_bg":"chart_content_bg2"} style={{
  280. left: parseInt(rpxToPx(46) + ''),
  281. right: parseInt(rpxToPx(46) + ''),
  282. top: parseInt(rpxToPx(60) + ''),
  283. bottom: parseInt(rpxToPx(60) + ''),
  284. width: parseInt(rpxToPx(658) + ''),
  285. }} /> :
  286. <LinearGradient style={{
  287. position: 'absolute',
  288. left: parseInt(rpxToPx(46) + ''),
  289. right: parseInt(rpxToPx(46) + ''),
  290. top: parseInt(rpxToPx(60) + ''),
  291. bottom: parseInt(rpxToPx(60) + ''),
  292. width: parseInt(rpxToPx(658) + ''),
  293. zIndex: -1
  294. }}
  295. colors={props.isFastSleep?['#1C1C1C', '#000000', '#1C1C1C']:['#000000', '#1C1C1C', '#000000']}
  296. start={{ x: 0, y: 0 }}
  297. end={{ x: 0, y: 1 }} />
  298. }
  299. <View>
  300. <Swiper
  301. current={current}
  302. onChange={onChange}
  303. style={{
  304. marginLeft: parseInt(rpxToPx(46) + ''),
  305. width: parseInt(rpxToPx(658) + ''),
  306. height: parseInt(rpxToPx(520) + ''),
  307. flexDirection: 'row', display: 'flex',
  308. }}>
  309. {
  310. calendars.map((item, index) => {
  311. return <SwiperItem key={index}>
  312. {
  313. showWeeklyItem(index) ?
  314. <WeekCalendarItem data={item} isCurrentWeek={index == calendars.length - 1} isFastSleep={props.isFastSleep} /> :
  315. <View style={{ width: parseInt(rpxToPx(658) + ''), flexShrink: 0 }} />
  316. }
  317. </SwiperItem>
  318. })
  319. }
  320. </Swiper>
  321. </View>
  322. {
  323. props.isFastSleep ? <View className="chart_times" style={{ marginTop: parseInt(rpxToPx(60) + ''), marginBottom: parseInt(rpxToPx(60) + '') }}>
  324. <Text className="chart_times_txt" style={{ left: 0, top: 0 }}>12:00</Text>
  325. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(100 - 7) + '') }}>18:00</Text>
  326. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(200 - 5) + '') }}>24:00</Text>
  327. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(300 - 5) + '') }}>06:00</Text>
  328. <Text className="chart_times_txt" style={{ left: 0, bottom: 0 }}>12:00</Text>
  329. </View> :
  330. <View className="chart_times" style={{ marginTop: parseInt(rpxToPx(60) + ''), marginBottom: parseInt(rpxToPx(60) + '') }}>
  331. <Text className="chart_times_txt" style={{ left: 0, top: 0 }}>00:00</Text>
  332. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(100 - 7) + '') }}>06:00</Text>
  333. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(200 - 5) + '') }}>12:00</Text>
  334. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(300 - 5) + '') }}>18:00</Text>
  335. <Text className="chart_times_txt" style={{ left: 0, bottom: 0 }}>24:00</Text>
  336. </View>
  337. }
  338. </View>
  339. </View>
  340. </View>
  341. })
  342. export default WeekCalendar;