MainFastEatCard.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. import { View, Text } from "@tarojs/components";
  2. import './MainCard.scss'
  3. import { useEffect, useRef, useState } from "react";
  4. import Modal from "@/components/layout/Modal.weapp";
  5. import { rpxToPx } from "@/utils/tools";
  6. import Rings, { RingCommon, BgRing, TargetRing, CurrentDot } from "@/features/trackTimeDuration/components/Rings";
  7. import dayjs from "dayjs";
  8. import moment from 'moment-timezone'
  9. import { MainColorType } from "@/context/themes/color";
  10. import { fastWindow } from "@/services/trackTimeDuration";
  11. import { useSelector } from "react-redux";
  12. import { jumpPage } from "../hooks/Common";
  13. import ConsolePicker from "./ConsolePicker";
  14. import { endFast, startFast } from "../actions/TrackTimeActions";
  15. import formatMilliseconds from "@/utils/format_time";
  16. let useNavigation;
  17. let Linking, PushNotification;
  18. let checkNotification;
  19. let min = 0
  20. let max = 0
  21. let defaultTimestamp = 0
  22. if (process.env.TARO_ENV == 'rn') {
  23. useNavigation = require("@react-navigation/native").useNavigation
  24. Linking = require('react-native').Linking;
  25. // JPush = require('jpush-react-native').default;
  26. PushNotification = require('react-native-push-notification')
  27. checkNotification = require('@/utils/native_permission_check').checkNotification;
  28. }
  29. export default function MainFastEatCard(props: { count: any }) {
  30. const [isFastMode, setIsFastMode] = useState(true)
  31. const [showModal, setShowModal] = useState(false)
  32. const [showTimePicker, setShowTimePicker] = useState(false);
  33. const limitPickerRef = useRef(null)
  34. const [operateType, setOperateType] = useState('startFast')
  35. const [btnDisable, setBtnDisable] = useState(false)
  36. const [logEvent, setLogEvent] = useState('LOG_ONCE');
  37. const [startScheduleTime, setStartScheduleTime] = useState('00:00')
  38. const [endScheduleTime, setEndScheduleTime] = useState('00:00')
  39. const [eatData, setEatData] = useState<any>(null)
  40. const [fastData, setFastData] = useState<any>(null)
  41. const [startTime, setStartTime] = useState<any>(null)
  42. const [endTime, setEndTime] = useState<any>(null)
  43. const [status, setStatus] = useState<any>('upcoming')
  44. const [loaded, setLoaded] = useState(false)
  45. const user = useSelector((state: any) => state.user);
  46. let navigation;
  47. if (useNavigation) {
  48. navigation = useNavigation()
  49. }
  50. useEffect(() => {
  51. fastWindow().then(res => {
  52. setLoaded(true)
  53. const { eat_win, fast_win } = res
  54. const fast = fast_win.fast
  55. setEatData(eat_win)
  56. setFastData(fast_win)
  57. setStartScheduleTime(fast_win.fast.schedule_start_time)
  58. setEndScheduleTime(fast_win.fast.schedule_end_time)
  59. var now = new Date().getTime()
  60. if ((fast.status == 'WAIT_FOR_END' || fast.target_start_time <= now && fast.target_end_time >= now) ||
  61. isCurrentTimeInRange(fast.schedule_start_time, fast.schedule_end_time)) {
  62. setIsFastMode(true)
  63. }
  64. else {
  65. setIsFastMode(false)
  66. }
  67. update(fast)
  68. })
  69. }, [user.isLogin])
  70. useEffect(() => {
  71. if (fastData) {
  72. update(fastData.fast)
  73. }
  74. }, [props.count])
  75. function refresh() {
  76. fastWindow().then(res => {
  77. const { eat_win, fast_win } = res
  78. const fast = fast_win.fast
  79. setEatData(eat_win)
  80. setFastData(fast_win)
  81. setStartScheduleTime(fast_win.fast.schedule_start_time)
  82. setEndScheduleTime(fast_win.fast.schedule_end_time)
  83. })
  84. }
  85. function update(fast) {
  86. var now = new Date().getTime()
  87. if (fast.status == 'WAIT_FOR_END') {
  88. setStatus('process')
  89. }
  90. else if ((fast.target_start_time <= now && fast.target_end_time >= now) || isCurrentTimeInRange(fast.schedule_start_time, fast.schedule_end_time)) {
  91. setStartTime(fast.schedule_start_time)
  92. setStatus('new')
  93. }
  94. else {
  95. setStatus('upcoming')
  96. }
  97. }
  98. const common: RingCommon = {
  99. useCase: 'ChooseScenario',
  100. radius: 115,
  101. lineWidth: 26,
  102. isFast: true,
  103. status: 'WAIT_FOR_START'
  104. }
  105. const bgRing: BgRing = {
  106. color: '#EAE9E9'
  107. }
  108. function scheduleRing() {
  109. var starts: any = startScheduleTime.split(':')
  110. var ends: any = endScheduleTime.split(':')
  111. const startSeconds: any = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
  112. const endSeconds: any = parseInt(ends[0] + '') * 60 + parseInt(ends[1] + '')
  113. const color = isFastMode ? '#9AE2FE' : '#FE810C66'
  114. const startArc = isFastMode ? startSeconds / 1440 * 2 * Math.PI - Math.PI / 2 : endSeconds / 1440 * 2 * Math.PI - Math.PI / 2
  115. const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
  116. const eatCount = 1440 - fastCount
  117. const durationArc = isFastMode ? fastCount / 1440 * 2 * Math.PI : eatCount / 1440 * 2 * Math.PI
  118. return {
  119. color,
  120. startArc,
  121. durationArc
  122. }
  123. }
  124. function targetRing() {
  125. if (status != 'process') {
  126. return null
  127. }
  128. var starts: any = startScheduleTime.split(':')
  129. var ends: any = endScheduleTime.split(':')
  130. const startSeconds: any = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
  131. const endSeconds: any = parseInt(ends[0] + '') * 60 + parseInt(ends[1] + '')
  132. const color = isFastMode ? '#9AE2FE' : '#FE810C66'
  133. var startArc = isFastMode ? startSeconds / 1440 * 2 * Math.PI - Math.PI / 2 : endSeconds / 1440 * 2 * Math.PI - Math.PI / 2
  134. const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
  135. const eatCount = 1440 - fastCount
  136. var durationArc = isFastMode ? fastCount / 1440 * 2 * Math.PI : eatCount / 1440 * 2 * Math.PI
  137. if (isFastMode) {
  138. var dt = new Date(fastData.fast.target_start_time)
  139. var realSeconds = dt.getHours() * 3600 + dt.getMinutes() * 60 + dt.getSeconds()
  140. startArc = realSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2
  141. durationArc = ((fastData.fast.target_end_time - fastData.fast.target_start_time) / 1000) / (1440 * 60) * 2 * Math.PI
  142. }
  143. return {
  144. color,
  145. startArc,
  146. durationArc,
  147. radius: isFastMode ? 90 : null,
  148. lineWidth: isFastMode ? 10 : null
  149. }
  150. }
  151. function realRing() {
  152. if (isFastMode) {
  153. if (status != 'upcoming') {
  154. var starts: any = startTime ? startTime.split(':') : startScheduleTime.split(':')
  155. const startSeconds = parseInt(starts[0] + '') * 3600 + parseInt(starts[1] + '') * 60
  156. const color = MainColorType.fast
  157. var startArc = startSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2
  158. var endSeconds = new Date().getHours() * 3600 + new Date().getMinutes() * 60 + new Date().getSeconds()
  159. if (endTime) {
  160. var ends: any = endTime.split(':')
  161. endSeconds = parseInt(ends[0] + '') * 3600 + parseInt(ends[1] + '') * 60
  162. }
  163. const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440 * 60
  164. var durationArc = fastCount / (1440 * 60) * 2 * Math.PI
  165. if (status == 'process') {
  166. var dt = new Date(fastData.fast.target_start_time)
  167. var realSeconds = dt.getHours() * 3600 + dt.getMinutes() * 60 + dt.getSeconds()
  168. startArc = realSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2
  169. durationArc = ((new Date().getTime() - fastData.fast.target_start_time) / 1000) / (1440 * 60) * 2 * Math.PI
  170. }
  171. return {
  172. color,
  173. startArc,
  174. durationArc,
  175. radius: status == 'process' ? 90 : null,
  176. lineWidth: status == 'process' ? 15 : null
  177. }
  178. }
  179. }
  180. else {
  181. if (isCurrentTimeInRange(endScheduleTime, startScheduleTime)) {
  182. var starts: any = endScheduleTime.split(':')
  183. const startSeconds = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
  184. const color = MainColorType.eat
  185. const startArc = startSeconds / 1440 * 2 * Math.PI - Math.PI / 2
  186. var endSeconds = new Date().getHours() * 60 + new Date().getMinutes()
  187. const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
  188. const durationArc = fastCount / 1440 * 2 * Math.PI
  189. return {
  190. color,
  191. startArc,
  192. durationArc,
  193. radius: 90,
  194. lineWidth: 10
  195. }
  196. }
  197. }
  198. }
  199. function isCurrentTimeInRange(start, end) {
  200. // 获取当前时间
  201. const now = new Date();
  202. const currentTime = now.getHours() * 60 + now.getMinutes(); // 当前时间转换为分钟
  203. // 将时间点转换为分钟
  204. const [startHour, startMinute] = start.split(':').map(Number);
  205. const [endHour, endMinute] = end.split(':').map(Number);
  206. const startTime = startHour * 60 + startMinute; // 开始时间转换为分钟
  207. const endTime = endHour * 60 + endMinute; // 结束时间转换为分钟
  208. // 处理跨日的情况
  209. if (endTime < startTime) {
  210. return currentTime >= startTime || currentTime <= endTime;
  211. }
  212. // 判断当前时间是否在范围内
  213. return currentTime >= startTime && currentTime <= endTime;
  214. }
  215. function getTimeToDestination(timeStr, isPasted) {
  216. // 获取当前时间
  217. const now = new Date();
  218. const currentHours = now.getHours();
  219. const currentMinutes = now.getMinutes();
  220. const currentSeconds = now.getSeconds();
  221. // 解析目标时间
  222. const [targetHours, targetMinutes] = timeStr.split(':').map(Number);
  223. // 计算时间差
  224. let hours = targetHours - currentHours;
  225. let minutes = targetMinutes - currentMinutes;
  226. let seconds = 60 - currentSeconds;
  227. if (minutes < 0) {
  228. minutes += 60;
  229. hours--;
  230. }
  231. if (hours < 0) {
  232. hours += 24;
  233. }
  234. if (seconds === 60) {
  235. seconds = 0;
  236. minutes++;
  237. }
  238. // 如果是过去的时间,则返回剩余时间
  239. if (isPasted) {
  240. hours = 24 - hours;
  241. minutes = 60 - minutes;
  242. seconds = 60 - seconds;
  243. }
  244. // 格式化输出
  245. return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  246. }
  247. function ring() {
  248. var offset = 0
  249. var hour = new Date().getHours()
  250. var minute = new Date().getMinutes()
  251. if (hour != new Date().getHours() || minute != new Date().getMinutes()) {
  252. offset = hour * 60 + minute - new Date().getHours() * 60 - new Date().getMinutes()
  253. }
  254. const currentDot: CurrentDot = {
  255. color: isFastMode ? MainColorType.fast : MainColorType.eat,
  256. lineWidth: 10,
  257. borderColor: '#fff',
  258. offset: offset,
  259. whiteIcon: true
  260. }
  261. return <Rings common={common} bgRing={bgRing} scheduleRing={scheduleRing()} targetRing={targetRing()} realRing={realRing()} currentDot={currentDot} canvasId={'smal11l'} />
  262. }
  263. function formatTime(format: string, timestamp?: number) {
  264. // var moment = require('moment-timezone');
  265. // if (current) {
  266. // if (timestamp) {
  267. // return moment(timestamp).tz(current.time.timezone.id).format(format)
  268. // }
  269. // return moment().tz(current.time.timezone.id).format(format)
  270. // }
  271. return dayjs().format(format)
  272. }
  273. function switchMode() {
  274. setIsFastMode(!isFastMode)
  275. }
  276. function getFastStatus() {
  277. switch (status) {
  278. case 'process':
  279. return 'Process'
  280. case 'new':
  281. return 'New Open'
  282. case 'upcoming':
  283. return 'Upcoming'
  284. }
  285. return ''
  286. }
  287. function getFastTime() {
  288. var milliSeconds = 0;
  289. switch (status) {
  290. case 'process':
  291. milliSeconds = new Date().getTime() - fastData.fast.real_start_time
  292. break;
  293. case 'new':
  294. return getTimeToDestination(fastData.fast.schedule_start_time, true)
  295. case 'upcoming':
  296. return getTimeToDestination(fastData.fast.schedule_start_time, false)
  297. }
  298. var seconds = Math.floor(milliSeconds / 1000)
  299. const hours = Math.floor(seconds / 3600);
  300. const minutes = Math.floor((seconds % 3600) / 60);
  301. const remainingSeconds = seconds % 60;
  302. return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
  303. }
  304. function getEatTime() {
  305. return getTimeToDestination(eatData.schedule_start_time, isCurrentTimeInRange(eatData.schedule_start_time, eatData.schedule_end_time))
  306. }
  307. function tapFastStart() {
  308. }
  309. function tapFastEnd() {
  310. }
  311. function tapStartLog() {
  312. if (status == 'upcoming'){
  313. return;
  314. }
  315. if (!user.isLogin) {
  316. jumpPage('/pages/account/ChooseAuth', 'ChooseAuth', navigation)
  317. return
  318. }
  319. defaultTimestamp = new Date().getTime()
  320. min = defaultTimestamp - 1 * 24 * 3600 * 1000
  321. max = defaultTimestamp
  322. setOperateType('startFast')
  323. setShowTimePicker(true)
  324. }
  325. function tapEndLog() {
  326. if (status != 'process') {
  327. return
  328. }
  329. defaultTimestamp = new Date().getTime()
  330. // defaultTimestamp = e ? new Date().getTime() : logEventTimestamp
  331. min = defaultTimestamp - 1 * 24 * 3600 * 1000
  332. max = defaultTimestamp
  333. setOperateType('endFast')
  334. setShowTimePicker(true)
  335. }
  336. function modalContent() {
  337. global.set_time = new Date().getTime()
  338. return <Modal
  339. testInfo={null}
  340. dismiss={() => {
  341. setShowTimePicker(false)
  342. }}
  343. confirm={() => { }}>
  344. {
  345. timePickerContent()
  346. }
  347. </Modal>
  348. }
  349. function timePickerContent() {
  350. var title = operateType == 'endFast' ? '结束断食' : '开始断食'
  351. var color = MainColorType.fast
  352. var endTimestamp = 0
  353. if (operateType == 'endFast') {
  354. endTimestamp = fastData.fast.target_end_time
  355. }
  356. var duration = fastData.fast.target_duration
  357. return <View className="modal_content">
  358. <ConsolePicker ref={limitPickerRef}
  359. themeColor={color}
  360. title={title}
  361. onCancel={() => {
  362. setShowTimePicker(false)
  363. }}
  364. min={min}
  365. max={max}
  366. current={defaultTimestamp}
  367. duration={duration}
  368. endTimestamp={endTimestamp}
  369. isFast={true}
  370. isEnd={operateType == 'endFast'}
  371. isTimeout={false}
  372. isLoading={btnDisable}
  373. onChange={(e) => {
  374. pickerConfirm(e, null)
  375. global.pauseIndexTimer = false
  376. }}
  377. />
  378. </View>
  379. }
  380. function pickerConfirm(t1: number, event: any) {
  381. if (btnDisable) {
  382. return
  383. }
  384. global.scenario = 'FAST'
  385. setBtnDisable(true)
  386. var date = new Date(t1)
  387. var setDate = new Date(global.set_time);
  388. date.setMilliseconds(setDate.getMilliseconds());
  389. date.setSeconds(setDate.getSeconds());
  390. t1 = date.getTime();
  391. if (operateType == 'startFast') {
  392. startFast(t1, fastData.fast.target_duration, event ? event : logEvent).then(res => {
  393. setBtnDisable(false)
  394. refresh()
  395. setShowTimePicker(false)
  396. }).catch(e => {
  397. setBtnDisable(false)
  398. })
  399. }
  400. else {
  401. endFast(t1, event ? event : logEvent).then(res => {
  402. setBtnDisable(false)
  403. refresh()
  404. setShowTimePicker(false)
  405. }).catch(e => {
  406. setBtnDisable(false)
  407. })
  408. }
  409. }
  410. if (!loaded) {
  411. return <View />
  412. }
  413. return <View style={{ alignItems: 'center', display: 'flex', flexDirection: 'column', width: rpxToPx(750), flexShrink: 0 }}>
  414. <View style={{ width: rpxToPx(750), }} />
  415. <View onClick={() => { setShowModal(true) }}>Fast Eat Night{props.count}</View>
  416. <View style={{ height: 40 }} />
  417. <View style={{ position: 'relative', }}>
  418. {
  419. ring()
  420. }
  421. <View className="ring_center">
  422. {
  423. isFastMode && <Text>{getFastStatus()}</Text>
  424. }
  425. <Text className="time1">{isFastMode ? getFastTime() : getEatTime()}</Text>
  426. <Text className="date1">{global.language == 'en' ? formatTime('dddd, MMM D') : formatTime('MMMD日 dddd')}</Text>
  427. </View>
  428. </View>
  429. <View>{isFastMode?formatMilliseconds(fastData.fast.target_duration):formatMilliseconds(eatData.target_end_time-eatData.target_start_time)}</View>
  430. {
  431. isFastMode && <View>
  432. <View className="log_row">
  433. {
  434. status == 'process' ? <View className="schedule_name">Fast starts</View> : <View className="schedule" onClick={tapFastStart}>
  435. <Text className="schedule_name">
  436. Fast starts:
  437. </Text>
  438. <Text className="schedule_time">
  439. {startScheduleTime}
  440. </Text>
  441. </View>
  442. }
  443. {
  444. status == 'process' ? <View className="schedule_time">{dayjs(fastData.fast.target_start_time).format('HH:mm')}</View> :
  445. <View onClick={tapStartLog} className={status == 'new' ? "fast_log_btn" : "fast_log_btn fast_log_btn_disable"}>Log{status == 'new' && <View className="badge" />}</View>
  446. }
  447. </View>
  448. <View className="log_row">
  449. <View className="schedule" onClick={tapFastEnd}>
  450. <Text className="schedule_name">
  451. Fast ends:
  452. </Text>
  453. <Text className="schedule_time">
  454. {status == 'process' ?dayjs(fastData.fast.target_end_time).format('HH:mm'):endScheduleTime}
  455. </Text>
  456. </View>
  457. <View onClick={tapEndLog} className={status == 'process' ? "fast_log_btn" : "fast_log_btn fast_log_btn_disable"}>Log</View>
  458. </View>
  459. </View>
  460. }
  461. <Text onClick={switchMode}>Switch</Text>
  462. {
  463. showModal && <Modal dismiss={() => setShowModal(false)}>
  464. <View style={{ width: 100, height: 100, backgroundColor: 'red' }}>{props.count}</View>
  465. </Modal>
  466. }
  467. {
  468. showTimePicker && modalContent()
  469. }
  470. </View>
  471. }