import { View, Text, Image } from "@tarojs/components"; import './MainCard.scss' import { useEffect, useRef, useState } from "react"; import Modal from "@/components/layout/Modal.weapp"; import { rpxToPx } from "@/utils/tools"; import Rings, { RingCommon, BgRing, TargetRing, CurrentDot } from "@/features/trackTimeDuration/components/Rings"; import dayjs from "dayjs"; import moment from 'moment-timezone' import { MainColorType } from "@/context/themes/color"; import { fastWindow, setSchedule, updateRecord } from "@/services/trackTimeDuration"; import { useDispatch, useSelector } from "react-redux"; import { jumpPage } from "../trackTimeDuration/hooks/Common"; import ConsolePicker from "../trackTimeDuration/components/ConsolePicker"; import { endFast, startFast } from "../trackTimeDuration/actions/TrackTimeActions"; import formatMilliseconds from "@/utils/format_time"; import TimePicker from "@/features/common/TimePicker"; import showAlert from "@/components/basic/Alert"; import showActionSheet from "@/components/basic/ActionSheet"; import { records } from "@/services/health"; import MainHistory from "./MainHistory"; import { WindowStatusType, WindowType } from "@/utils/types"; import { durationArc, isCurrentTimeInRange, startArc } from "./util"; import { setMode } from "@/store/health"; import { IconSwitch1, IconSwitch2 } from "@/components/basic/Icons"; import { getScenario, getWindowStatus } from "./hooks/health_hooks"; let useNavigation; let Linking, PushNotification; let checkNotification; let min = 0 let max = 0 let defaultTimestamp = 0 let useActionSheet; if (process.env.TARO_ENV == 'rn') { useNavigation = require("@react-navigation/native").useNavigation Linking = require('react-native').Linking; // JPush = require('jpush-react-native').default; PushNotification = require('react-native-push-notification') checkNotification = require('@/utils/native_permission_check').checkNotification; useActionSheet = require('@expo/react-native-action-sheet').useActionSheet } export default function MainFastEatCard(props: { count: any, typeChanged: Function, id: number, onClick: Function }) { const [isFastMode, setIsFastMode] = useState(true) const [showModal, setShowModal] = useState(false) const [showTimePicker, setShowTimePicker] = useState(false); const limitPickerRef = useRef(null) const [operateType, setOperateType] = useState('startFast') const [btnDisable, setBtnDisable] = useState(false) const [logEvent, setLogEvent] = useState('LOG_ONCE'); const [eatData, setEatData] = useState(null) const [fastData, setFastData] = useState(null) const [startTime, setStartTime] = useState(null) const [endTime, setEndTime] = useState(null) const [status, setStatus] = useState('upcoming') const [showPicker, setShowPicker] = useState(false) const [isStart, setIsStart] = useState(true) const user = useSelector((state: any) => state.user); const health = useSelector((state: any) => state.health); const dispatch = useDispatch() let navigation, showActionSheetWithOptions; if (useNavigation) { navigation = useNavigation() showActionSheetWithOptions = useActionSheet() } useEffect(() => { if (health.mode == 'FAST') { setIsFastMode(true) } else if (health.mode == 'EAT') { setIsFastMode(false) } }, [health.mode]) useEffect(() => { const { fast, eat } = health.windows.fast_eat var now = new Date().getTime() if ((fast.status == 'WAIT_FOR_END' || fast.target.start_time <= now && fast.target.end_time >= now) || isCurrentTimeInRange(fast.period.start_time, fast.period.end_time)) { setIsFastMode(true) } else { setIsFastMode(false) } setEatData(eat) setFastData(fast) update(fast) }, [user.isLogin]) useEffect(() => { if (fastData) { update(fastData) } }, [props.count]) useEffect(() => { if (health.selTab == 1) { dispatch(setMode(isFastMode ? 'FAST' : 'EAT')) } }, [health.selTab, isFastMode]) function update(fast) { var now = new Date().getTime() if (fast.status == 'WAIT_FOR_END') { setStatus('process') } else if ((fast.target.start_timestamp <= now && fast.target.end_timestamp >= now) || isCurrentTimeInRange(fast.period.start_time, fast.period.end_time)) { setStartTime(fast.period.start_time) setStatus('new') } else { setStatus('upcoming') } } const common: RingCommon = { useCase: 'ChooseScenario', radius: 37, lineWidth: 14, isFast: true, status: 'WAIT_FOR_START' } const bgRing: BgRing = { color: MainColorType.ringBg } function scheduleRing() { const { fast, eat } = health.windows.fast_eat var starts: any = fast.period.start_time.split(':') var ends: any = fast.period.end_time.split(':') const startSeconds: any = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '') const endSeconds: any = parseInt(ends[0] + '') * 60 + parseInt(ends[1] + '') const color = isFastMode ? MainColorType.fastLight : MainColorType.eatLight const startArc = isFastMode ? startSeconds / 1440 * 2 * Math.PI - Math.PI / 2 : endSeconds / 1440 * 2 * Math.PI - Math.PI / 2 const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440 const eatCount = 1440 - fastCount const durationArc = isFastMode ? fastCount / 1440 * 2 * Math.PI : eatCount / 1440 * 2 * Math.PI return { color, startArc, durationArc } } function targetRing() { if (status != 'process') { return null } const { fast, eat } = health.windows.fast_eat var starts: any = fast.period.start_time.split(':') var ends: any = fast.period.end_time.split(':') const startSeconds: any = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '') const endSeconds: any = parseInt(ends[0] + '') * 60 + parseInt(ends[1] + '') const color = isFastMode ? '#9AE2FE' : '#FE810C66' var startArc = isFastMode ? startSeconds / 1440 * 2 * Math.PI - Math.PI / 2 : endSeconds / 1440 * 2 * Math.PI - Math.PI / 2 const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440 const eatCount = 1440 - fastCount var durationArc = isFastMode ? fastCount / 1440 * 2 * Math.PI : eatCount / 1440 * 2 * Math.PI if (isFastMode) { var dt = new Date(fastData.target.start_time) var realSeconds = dt.getHours() * 3600 + dt.getMinutes() * 60 + dt.getSeconds() startArc = realSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2 durationArc = ((fastData.target.end_time - fastData.target.start_time) / 1000) / (1440 * 60) * 2 * Math.PI } return { color, startArc, durationArc, radius: isFastMode ? 90 : null, lineWidth: isFastMode ? 10 : null } } function getRealArc(time) { var date = new Date(time); var hour = date.getHours(); var minute = date.getMinutes(); var second = date.getSeconds(); return (hour * 3600 + minute * 60 + second) / (24 * 3600) * 2 * Math.PI - Math.PI / 2.0; } function getRealDurationArc(start, end) { var duration = (end - start) / 1000; return duration / (24 * 3600) * 2 * Math.PI; } function realRing() { const status = getWindowStatus(health.windows, isFastMode ? 'FAST' : 'EAT') const scenario = getScenario(health.windows, isFastMode ? 'FAST' : 'EAT') if (status == WindowStatusType.upcoming) { return { color: '#cccccc', startArc: getRealArc(new Date().getTime()), durationArc: getRealDurationArc(new Date().getTime(), scenario.target.start_timestamp), } } return { color: isFastMode ? MainColorType.fast : MainColorType.eat, startArc: getRealArc(scenario.target.start_timestamp), durationArc: getRealDurationArc(scenario.target.start_timestamp, new Date().getTime()) } } function getTimeToDestination(timeStr, isPasted) { // 获取当前时间 const now = new Date(); const currentHours = now.getHours(); const currentMinutes = now.getMinutes(); const currentSeconds = now.getSeconds(); // 解析目标时间 const [targetHours, targetMinutes] = timeStr.split(':').map(Number); // 计算时间差 let hours = targetHours - currentHours; let minutes = targetMinutes - currentMinutes; let seconds = 60 - currentSeconds; if (minutes < 0) { minutes += 60; hours--; } if (hours < 0) { hours += 24; } if (seconds === 60) { seconds = 0; minutes++; } // 如果是过去的时间,则返回剩余时间 if (isPasted) { hours = 24 - hours; minutes = 60 - minutes; seconds = 60 - seconds; } // 格式化输出 return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } function ring() { var offset = 0 var hour = new Date().getHours() var minute = new Date().getMinutes() if (hour != new Date().getHours() || minute != new Date().getMinutes()) { offset = hour * 60 + minute - new Date().getHours() * 60 - new Date().getMinutes() } // const currentDot: CurrentDot = { // color: isFastMode ? MainColorType.fast : MainColorType.eat, // lineWidth: 10, // borderColor: '#fff', // offset: offset, // whiteIcon: true // } return } function formatTime(format: string, timestamp?: number) { // var moment = require('moment-timezone'); // if (current) { // if (timestamp) { // return moment(timestamp).tz(current.time.timezone.id).format(format) // } // return moment().tz(current.time.timezone.id).format(format) // } return dayjs().format(format) } function switchMode() { const mode = !isFastMode setIsFastMode(mode) props.typeChanged(mode ? WindowType.fast : WindowType.eat) } function getFastStatus() { switch (status) { case 'process': return 'Process' case 'new': return 'New Open' case 'upcoming': return 'Upcoming' } return '' } function getFastTime() { var milliSeconds = 0; switch (status) { case 'process': milliSeconds = new Date().getTime() - fastData.real_start_time break; case 'new': return getTimeToDestination(fastData.period.start_time, true) case 'upcoming': return getTimeToDestination(fastData.period.start_time, false) } var seconds = Math.floor(milliSeconds / 1000) const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const remainingSeconds = seconds % 60; return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; } function getEatTime() { return getTimeToDestination(eatData.period.start_time, isCurrentTimeInRange(eatData.period.start_time, eatData.period.end_time)) } function tapFastStart() { setIsStart(true) setShowPicker(true) } function tapFastEnd() { setIsStart(false) setShowPicker(true) } function tapStartLog() { if (status == 'upcoming') { return; } if (!user.isLogin) { jumpPage('/pages/account/ChooseAuth', 'ChooseAuth', navigation) return } defaultTimestamp = new Date().getTime() min = defaultTimestamp - 1 * 24 * 3600 * 1000 max = defaultTimestamp setOperateType('startFast') setShowTimePicker(true) } function tapEndLog() { if (status != 'process') { return } defaultTimestamp = new Date().getTime() // defaultTimestamp = e ? new Date().getTime() : logEventTimestamp min = defaultTimestamp - 1 * 24 * 3600 * 1000 max = defaultTimestamp setOperateType('endFast') setShowTimePicker(true) } function modalContent() { global.set_time = new Date().getTime() return { setShowTimePicker(false) }} confirm={() => { }}> { timePickerContent() } } function timeContent() { return { setShowPicker(false) }} confirm={() => { }}> { pickerContent() } } function timePickerContent() { var title = operateType == 'endFast' ? '结束断食' : '开始断食' var color = MainColorType.fast var endTimestamp = 0 if (operateType == 'endFast') { endTimestamp = fastData.target.end_time } var duration = fastData.target.duration return { setShowTimePicker(false) }} min={min} max={max} current={defaultTimestamp} duration={duration} endTimestamp={endTimestamp} isFast={true} isEnd={operateType == 'endFast'} isTimeout={false} isLoading={btnDisable} onChange={(e) => { pickerConfirm(e, null) global.pauseIndexTimer = false }} /> } function pickerContent() { const timestamp = isStart ? fastData.target.start_time : fastData.target.end_time const strTime = dayjs(timestamp).format('HH:mm') return { confirmPickerTime(e) }} cancel={() => { setShowPicker(false) }} /> } function confirmPickerTime(strTime) { if (status != 'process') { setSchedule({ code: isStart ? 'FAST_START' : 'FAST_END', time: strTime }).then(res => { setShowPicker(false) }) } else { var startTime = dayjs(fastData.target.start_time).format('HH:mm:ss') var endTime = strTime + ':00' console.log(startTime, endTime) if (startTime == endTime) { showAlert({ title: '', content: '开始时间不能与结束时间相同' }) return; } updateRecord({ fast: { target_duration: getIntervalSeconds(startTime, endTime) * 1000 } }, fastData.id).then(res => { setShowPicker(false) }) } } function getIntervalSeconds(time1, time2) { // 将时间字符串转换为 Date 对象 const date1 = new Date(`2000-01-01T${time1}Z`); const date2 = new Date(`2000-01-01T${time2}Z`); // 计算两个 Date 对象之间的时间差 let intervalMs = date2.getTime() - date1.getTime(); // 如果 time2 比 time1 小, 说明跨天了, 需要加上一天的毫秒数 if (date2 < date1) { intervalMs += 24 * 60 * 60 * 1000; } // 返回间隔秒数 return Math.floor(intervalMs / 1000); } function pickerConfirm(t1: number, event: any) { if (btnDisable) { return } global.scenario = 'FAST' setBtnDisable(true) var date = new Date(t1) var setDate = new Date(global.set_time); date.setMilliseconds(setDate.getMilliseconds()); date.setSeconds(setDate.getSeconds()); t1 = date.getTime(); if (operateType == 'startFast') { startFast(t1, fastData.target_duration, event ? event : logEvent).then(res => { setBtnDisable(false) setShowTimePicker(false) }).catch(e => { setBtnDisable(false) }) } else { endFast(t1, event ? event : logEvent).then(res => { setBtnDisable(false) setShowTimePicker(false) }).catch(e => { setBtnDisable(false) }) } } function goAddEat(meal, index) { if (!enableMeal(meal)) { return; } jumpPage('/pages/clock/AddEat?meal=' + JSON.stringify(eatData.meals[index])) } function enableMeal(meal) { if (meal.real_start_time > 0) { return true } if (meal.target.start_time > new Date().getTime()) { return true } return true } function more() { showActionSheet({ showActionSheetWithOptions: showActionSheetWithOptions, title: 'Oprate Title', itemList: [ 'Add Snack', '自定义餐次列表', ], success: (res) => { switch (res) { case 0: break; case 1: jumpPage('/_health/pages/edit_schedule') break; } } }); } function switchIcon() { if (isFastMode) { if (status != 'upcoming') { return } return } if (status != 'upcoming') { return } return } function switchBtn() { var bgColor = '' if (isFastMode) { if (status != 'upcoming') { bgColor = '#fff' } else { bgColor = MainColorType.eat } } else { if (status != 'upcoming') { bgColor = MainColorType.fast } else { bgColor = '#fff' } } return { switchIcon() } } if (!fastData) return return props.onClick()}> { ring() } {isFastMode ? 'Fast' : 'Eat'} {/* { isFastMode && {getFastStatus()} } {isFastMode ? getFastTime() : getEatTime()} {global.language == 'en' ? formatTime('dddd, MMM D') : formatTime('MMMD日 dddd')} */} {/* {isFastMode ? formatMilliseconds(health.windows.fast_eat.fast.target.duration) : formatMilliseconds(health.windows.fast_eat.eat.target.duration)} */} {/* { isFastMode && { status == 'process' ? Fast starts : Fast starts: {startScheduleTime} } { status == 'process' ? {dayjs(fastData.target.start_time).format('HH:mm')} : Log{status == 'new' && } } Fast ends: {status == 'process' ? dayjs(fastData.target.end_time).format('HH:mm') : endScheduleTime} Log } { !isFastMode && { eatData.meals.map((item, index) => { return { item.real_start_time && item.media && item.media.length > 0 && item.media[0].url && } {item.name} {item.real_start_time ? dayjs(item.real_start_time).format('HH:mm') + ' - ' + dayjs(item.real_end_time).format('HH:mm') : item.period.start_time} { item.real_start_time ? 已记录 : goAddEat(item, index)} className={enableMeal(item) ? "fast_log_btn fast_log_eat_btn" : "fast_log_btn fast_log_btn_disable"}>Add } }) } } */} {/* { switchBtn() } */} {/* { !isFastMode && 更多 } */} {/* */} { showModal && setShowModal(false)}> {props.count} } { showTimePicker && modalContent() } { showPicker && timeContent() } }