WeekCalendar.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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 } 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. }, [])
  35. global.refrehWeekly = () => {
  36. // getRecords()
  37. if (isLoading) return
  38. pageIndex = 0;
  39. setIsLoading(true)
  40. var timestamp = TimeFormatter.getMondayTimestamp()
  41. var now = new Date()
  42. if (now.getDay() == 0 && now.getHours() >= 0 && now.getHours() < 12) {
  43. timestamp -= 24 * 3600 * 1000 * 7
  44. }
  45. var list: any = []
  46. var offset = 12 * 3600 * 1000 + pageIndex * pageSize * 24 * 3600 * 1000
  47. if (!props.isFastSleep){
  48. offset = pageIndex * pageSize * 24 * 3600 * 1000
  49. }
  50. for (var i = 0; i < pageSize; i++) {
  51. list.push(`${i + pageIndex * pageSize},${timestamp - 7 * 24 * 3600 * 1000 * i - offset},${timestamp - 7 * 24 * 3600 * 1000 * i + 7 * 24 * 3600 * 1000 - offset}`)
  52. }
  53. clockSummaryStats({ times: list.join(';') }).then(res => {
  54. var list = (res as any).data.reverse()
  55. if (pageIndex == 0) {
  56. setCalendars(list)
  57. setSummary(list[current].summary_stats ? list[current].summary_stats : null)
  58. setCalendarData(list[current])
  59. setIsLoading(false)
  60. }
  61. })
  62. }
  63. function getRecords() {
  64. if (isLoading) return
  65. pageIndex = 0;
  66. setIsLoading(true)
  67. var timestamp = TimeFormatter.getMondayTimestamp()
  68. var now = new Date()
  69. if (now.getDay() == 0 && now.getHours() >= 0 && now.getHours() < 12) {
  70. timestamp -= 24 * 3600 * 1000 * 7
  71. }
  72. var list: any = []
  73. var offset = 12 * 3600 * 1000 + pageIndex * pageSize * 24 * 3600 * 1000
  74. if (!props.isFastSleep){
  75. offset = pageIndex * pageSize * 24 * 3600 * 1000
  76. }
  77. for (var i = 0; i < pageSize; i++) {
  78. list.push(`${i + pageIndex * pageSize},${timestamp - 7 * 24 * 3600 * 1000 * i - offset},${timestamp - 7 * 24 * 3600 * 1000 * i + 7 * 24 * 3600 * 1000 - offset}`)
  79. }
  80. clockSummaryStats({ times: list.join(';') }).then(res => {
  81. var list = (res as any).data.reverse()
  82. if (list.length > 1) {
  83. lastFastValue = list[list.length - 2].summary_stats.fast.avg
  84. lastSleepValue = list[list.length - 2].summary_stats.sleep.avg
  85. }
  86. if (pageIndex == 0) {
  87. setCalendars(list)
  88. setCurrent(list.length - 1)
  89. setSummary(list[list.length - 1].summary_stats ? list[list.length - 1].summary_stats : null)
  90. setCalendarData(list[list.length - 1])
  91. setMinTime((res as any).extra.real_start_time_min)
  92. setLeft((list.length - 1) * parseInt(rpxToPx(658) + ''))
  93. setIsLoading(false);
  94. if (ref.current) {
  95. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  96. setTimeout(() => {
  97. (ref.current as any).scrollToOffset((list.length - 1) * parseInt(rpxToPx(658) + ''), false);
  98. }, 500)
  99. }
  100. }
  101. else {
  102. }
  103. })
  104. // clockSummaryStats({start:new Date().getTime()-7*24*3600*1000,end:new Date().getTime()}).then(res => { })
  105. }
  106. function dragStart(e) {
  107. // fingerDrag = true
  108. }
  109. function dragEnd(e) {
  110. // fingerDrag = false
  111. }
  112. function onScroll(e) {
  113. // if (!fingerDrag && process.env.TARO_ENV == 'weapp') return
  114. if (lastOffset && Math.abs(lastOffset - e.detail.scrollLeft) > 300) {
  115. setLeft(lastOffset)
  116. return
  117. }
  118. // isScrolling = true
  119. var x = e.detail.scrollLeft + 5
  120. lastOffset = e.detail.scrollLeft
  121. // var page = parseInt(x / parseInt(rpxToPx(658) + '') + '')
  122. var page = Math.round(x / parseInt(rpxToPx(658) + ''))
  123. console.log(page, x)
  124. if (current != page && page < calendars.length && page >= 0) {
  125. lastFastValue = calendars[current].summary_stats.fast.avg
  126. lastSleepValue = calendars[current].summary_stats.sleep.avg
  127. setCurrent(page)
  128. setSummary(calendars[page].summary_stats ? calendars[page].summary_stats : null)
  129. setCalendarData(calendars[page])
  130. // if (page <= 0) {
  131. // getRecords();
  132. // }
  133. }
  134. // if (scrollTimer)
  135. // clearTimeout(scrollTimer);
  136. // scrollTimer = setTimeout(() => {
  137. // isScrolling = false
  138. // fingerDrag = false;
  139. // }, 300) as any
  140. }
  141. function onChange(e) {
  142. var page = e.detail.current
  143. lastFastValue = calendars[current].summary_stats.fast.avg
  144. lastSleepValue = calendars[current].summary_stats.sleep.avg
  145. setCurrent(page)
  146. setSummary(calendars[page].summary_stats ? calendars[page].summary_stats : null)
  147. setCalendarData(calendars[page])
  148. }
  149. function getDate(timestamp) {
  150. var date = new Date(timestamp)
  151. if (global.language == 'en') {
  152. return dayjs(timestamp).format('MMM D')
  153. }
  154. return `${date.getMonth() + 1}月${date.getDate()}日`
  155. }
  156. function getWeekDuration() {
  157. if (global.language == 'en') {
  158. if (!props.isFastSleep) {
  159. return `${t('feature.common.day_of_week_full.mon')}, ${getDate(calendarData.start)} - ${t('feature.common.day_of_week_full.mon')}, ${getDate(calendarData.end)}`
  160. }
  161. 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)}`
  162. }
  163. if (!props.isFastSleep) {
  164. return `${getDate(calendarData.start)} ${t('feature.common.day_of_week_full.mon')} - ${getDate(calendarData.end)} ${t('feature.common.day_of_week_full.mon')}`
  165. }
  166. 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')}`
  167. }
  168. function getWeekIndex() {
  169. // style={{ opacity: current == calendars.length - 1 ? 1 : 0 }}
  170. if (current == calendars.length - 1) {
  171. return t('feature.common.week_desc.current_week')
  172. }
  173. else if (current == calendars.length - 2) {
  174. return t('feature.common.week_desc.last_week')
  175. }
  176. else {
  177. return t('feature.common.week_desc.weeks_ago', { index: calendars.length - current - 1 })
  178. }
  179. }
  180. function showWeeklyItem(index) {
  181. if (process.env.TARO_ENV == 'weapp') {
  182. if (index >= current - 1 && index <= current + 1) {
  183. return true
  184. }
  185. return false
  186. }
  187. if (index >= current - 2 && index <= current + 2) {
  188. return true
  189. }
  190. return false
  191. }
  192. if (!calendarData)
  193. return <View />
  194. return <View style={{ display: 'flex', flexDirection: 'column' }}>
  195. <View className="calendar_summary_top">
  196. <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
  197. {
  198. !summary || summary.fast.avg == 0 ? <View className="calendar-empty-bg" style={{ borderColor: props.isFastSleep ? ColorType.fast : ColorType.food }}>
  199. <View className="calendar-empty-line" style={{ backgroundColor: props.isFastSleep ? ColorType.fast : ColorType.food }} />
  200. </View> :
  201. <View className={lastFastValue > summary.fast.avg ? 'calendar-arrow-bg-down' : 'calendar-arrow-bg'} style={{ backgroundColor: props.isFastSleep ? ColorType.fast : ColorType.food }}>
  202. <IconBigArrow color="#fff" width={rpxToPx(32)} />
  203. </View>
  204. }
  205. <View className="calendar_summary_item">
  206. <Text className="calendar_summary_title">{props.isFastSleep ? t('feature.track_time_duration.weekly.fast_average') : t('feature.track_time_duration.weekly.eat_average')}</Text>
  207. <Text className="calendar_summary_value" style={{ color: props.isFastSleep ? ColorType.fast : ColorType.food }}>{!summary || summary.fast.avg == 0 ? t('feature.track_time_duration.record_fast_sleep.none') : TimeFormatter.calculateTimeDifference(new Date().getTime(), new Date().getTime() + summary.fast.avg)}</Text>
  208. </View>
  209. </View>
  210. <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
  211. {
  212. !summary || summary.sleep.avg == 0 ? <View className="calendar-empty-bg" style={{ borderColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>
  213. <View className="calendar-empty-line" style={{ backgroundColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }} />
  214. </View> :
  215. <View className={lastSleepValue > summary.sleep.avg ? 'calendar-arrow-bg-down' : 'calendar-arrow-bg'} style={{ backgroundColor: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>
  216. <IconBigArrow color="#fff" width={rpxToPx(32)} />
  217. </View>
  218. }
  219. <View className="calendar_summary_item">
  220. <Text className="calendar_summary_title">{props.isFastSleep ? t('feature.track_time_duration.weekly.sleep_average') : t('feature.track_time_duration.weekly.wake_average')}</Text>
  221. <Text className="calendar_summary_value" style={{ color: props.isFastSleep ? ColorType.sleep : ColorType.activity }}>{!summary || summary.sleep.avg == 0 ? t('feature.track_time_duration.record_fast_sleep.none') : TimeFormatter.calculateTimeDifference(new Date().getTime(), new Date().getTime() + summary.sleep.avg)}</Text>
  222. </View>
  223. </View>
  224. </View>
  225. <View style={{ display: 'flex', flexDirection: 'row', marginBottom: rpxToPx(16), alignItems: 'center' }}>
  226. <View className="calendar_this_week" >{getWeekIndex()}</View>
  227. <Text className="calendar_summary_desc">{getWeekDuration()}</Text>
  228. </View>
  229. <View className="chart_bg">
  230. <View className="chart_bg">
  231. {
  232. process.env.TARO_ENV == 'weapp' ? <View className="chart_content_bg" style={{
  233. left: parseInt(rpxToPx(46) + ''),
  234. right: parseInt(rpxToPx(46) + ''),
  235. top: parseInt(rpxToPx(60) + ''),
  236. bottom: parseInt(rpxToPx(60) + ''),
  237. width: parseInt(rpxToPx(658) + ''),
  238. }} /> :
  239. <LinearGradient style={{
  240. position: 'absolute',
  241. left: parseInt(rpxToPx(46) + ''),
  242. right: parseInt(rpxToPx(46) + ''),
  243. top: parseInt(rpxToPx(60) + ''),
  244. bottom: parseInt(rpxToPx(60) + ''),
  245. width: parseInt(rpxToPx(658) + ''),
  246. zIndex: -1
  247. }}
  248. colors={['#1C1C1C', '#000000', '#1C1C1C']}
  249. start={{ x: 0, y: 0 }}
  250. end={{ x: 0, y: 1 }} />
  251. }
  252. <View>
  253. <Swiper
  254. current={current}
  255. onChange={onChange}
  256. style={{
  257. marginLeft: parseInt(rpxToPx(46) + ''),
  258. width: parseInt(rpxToPx(658) + ''),
  259. height: parseInt(rpxToPx(520) + ''),
  260. flexDirection: 'row', display: 'flex',
  261. }}>
  262. {
  263. calendars.map((item, index) => {
  264. return <SwiperItem key={index}>
  265. {
  266. showWeeklyItem(index) ?
  267. <WeekCalendarItem data={item} isCurrentWeek={index == calendars.length - 1} isFastSleep={props.isFastSleep} /> :
  268. <View style={{ width: parseInt(rpxToPx(658) + ''), flexShrink: 0 }} />
  269. }
  270. </SwiperItem>
  271. })
  272. }
  273. </Swiper>
  274. </View>
  275. {
  276. props.isFastSleep ? <View className="chart_times" style={{ marginTop: parseInt(rpxToPx(60) + ''), marginBottom: parseInt(rpxToPx(60) + '') }}>
  277. <Text className="chart_times_txt" style={{ left: 0, top: 0 }}>12:00</Text>
  278. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(100 - 7) + '') }}>18:00</Text>
  279. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(200 - 5) + '') }}>24:00</Text>
  280. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(300 - 5) + '') }}>06:00</Text>
  281. <Text className="chart_times_txt" style={{ left: 0, bottom: 0 }}>12:00</Text>
  282. </View> :
  283. <View className="chart_times" style={{ marginTop: parseInt(rpxToPx(60) + ''), marginBottom: parseInt(rpxToPx(60) + '') }}>
  284. <Text className="chart_times_txt" style={{ left: 0, top: 0 }}>00:00</Text>
  285. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(100 - 7) + '') }}>06:00</Text>
  286. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(200 - 5) + '') }}>12:00</Text>
  287. <Text className="chart_times_txt" style={{ left: 0, top: parseInt(rpxToPx(300 - 5) + '') }}>18:00</Text>
  288. <Text className="chart_times_txt" style={{ left: 0, bottom: 0 }}>24:00</Text>
  289. </View>
  290. }
  291. </View>
  292. </View>
  293. </View>
  294. })
  295. export default WeekCalendar;