Keep OTA update button disabled while updating
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
||||||
import { useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import Taro, { useDidShow, useShareAppMessage } from '@tarojs/taro'
|
import Taro, { useDidHide, useDidShow, useShareAppMessage } from '@tarojs/taro'
|
||||||
import { authorizeParentPhone, clearToken, getParent, getToken, Parent } from '@/services/auth'
|
import { authorizeParentPhone, clearToken, getParent, getToken, Parent } from '@/services/auth'
|
||||||
import { DEVICE_UNAVAILABLE_MESSAGE } from '@/services/api'
|
import { DEVICE_UNAVAILABLE_MESSAGE } from '@/services/api'
|
||||||
import { getCurrentUserId } from '@/services/session'
|
import { getCurrentUserId } from '@/services/session'
|
||||||
@@ -48,6 +48,22 @@ interface MenuItem {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FIRMWARE_STATUS_POLL_INTERVAL_MS = 3000
|
||||||
|
const FIRMWARE_UPDATE_IN_PROGRESS_STATUSES = new Set(['sent', 'accepted', 'updating'])
|
||||||
|
const FIRMWARE_UPDATE_SUCCESS_STATUSES = new Set(['success', 'completed'])
|
||||||
|
|
||||||
|
function normalizeFirmwareUpdateStatus(status?: string | null): string {
|
||||||
|
return String(status || '').trim().toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFirmwareUpdateInProgress(status?: string | null): boolean {
|
||||||
|
return FIRMWARE_UPDATE_IN_PROGRESS_STATUSES.has(normalizeFirmwareUpdateStatus(status))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFirmwareUpdateSuccessful(status?: string | null): boolean {
|
||||||
|
return FIRMWARE_UPDATE_SUCCESS_STATUSES.has(normalizeFirmwareUpdateStatus(status))
|
||||||
|
}
|
||||||
|
|
||||||
export default function Sleep() {
|
export default function Sleep() {
|
||||||
const systemBanner = useSystemBanner()
|
const systemBanner = useSystemBanner()
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
@@ -89,11 +105,22 @@ export default function Sleep() {
|
|||||||
const [familyMemberDisplayName, setFamilyMemberDisplayName] = useState('')
|
const [familyMemberDisplayName, setFamilyMemberDisplayName] = useState('')
|
||||||
const [parentInfo, setParentInfo] = useState<Parent | null>(null)
|
const [parentInfo, setParentInfo] = useState<Parent | null>(null)
|
||||||
const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false)
|
const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false)
|
||||||
|
const firmwareStatusPollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||||
|
const firmwareStatusPollDeviceIdRef = useRef('')
|
||||||
|
const isFirmwareStatusPollingRef = useRef(false)
|
||||||
|
|
||||||
useDidShow(() => {
|
useDidShow(() => {
|
||||||
void loadData()
|
void loadData()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useDidHide(() => {
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => () => {
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
}, [])
|
||||||
|
|
||||||
useShareAppMessage(() => {
|
useShareAppMessage(() => {
|
||||||
const targetName = currentChild?.child_name || binding?.device_id || '孩子'
|
const targetName = currentChild?.child_name || binding?.device_id || '孩子'
|
||||||
return {
|
return {
|
||||||
@@ -145,11 +172,92 @@ export default function Sleep() {
|
|||||||
|
|
||||||
const currentChildName = currentChild?.child_name || '未设置'
|
const currentChildName = currentChild?.child_name || '未设置'
|
||||||
|
|
||||||
|
function clearFirmwareStatusPollTimer() {
|
||||||
|
if (firmwareStatusPollTimerRef.current) {
|
||||||
|
clearTimeout(firmwareStatusPollTimerRef.current)
|
||||||
|
firmwareStatusPollTimerRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFirmwareStatusPolling() {
|
||||||
|
clearFirmwareStatusPollTimer()
|
||||||
|
firmwareStatusPollDeviceIdRef.current = ''
|
||||||
|
isFirmwareStatusPollingRef.current = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleFirmwareStatusPoll(deviceId: string) {
|
||||||
|
clearFirmwareStatusPollTimer()
|
||||||
|
firmwareStatusPollTimerRef.current = setTimeout(() => {
|
||||||
|
void pollFirmwareStatus(deviceId)
|
||||||
|
}, FIRMWARE_STATUS_POLL_INTERVAL_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
function startFirmwareStatusPolling(deviceId: string) {
|
||||||
|
const normalizedDeviceId = String(deviceId || '').trim()
|
||||||
|
if (!normalizedDeviceId) return
|
||||||
|
|
||||||
|
firmwareStatusPollDeviceIdRef.current = normalizedDeviceId
|
||||||
|
scheduleFirmwareStatusPoll(normalizedDeviceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncFirmwareStatusPolling(deviceId: string, nextFirmwareStatus?: DeviceFirmwareStatus | null) {
|
||||||
|
const normalizedDeviceId = String(deviceId || '').trim()
|
||||||
|
if (!normalizedDeviceId) {
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFirmwareUpdateInProgress(nextFirmwareStatus?.update_status)) {
|
||||||
|
startFirmwareStatusPolling(normalizedDeviceId)
|
||||||
|
} else {
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollFirmwareStatus(deviceId: string) {
|
||||||
|
const normalizedDeviceId = String(deviceId || '').trim()
|
||||||
|
if (
|
||||||
|
!normalizedDeviceId ||
|
||||||
|
firmwareStatusPollDeviceIdRef.current !== normalizedDeviceId ||
|
||||||
|
isFirmwareStatusPollingRef.current
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirmwareStatusPollingRef.current = true
|
||||||
|
try {
|
||||||
|
const nextFirmwareStatus = await getDeviceFirmwareStatus(normalizedDeviceId)
|
||||||
|
if (firmwareStatusPollDeviceIdRef.current !== normalizedDeviceId) return
|
||||||
|
|
||||||
|
setFirmwareStatus(nextFirmwareStatus)
|
||||||
|
if (isFirmwareUpdateInProgress(nextFirmwareStatus?.update_status)) {
|
||||||
|
scheduleFirmwareStatusPoll(normalizedDeviceId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
const nextStatus = normalizeFirmwareUpdateStatus(nextFirmwareStatus?.update_status)
|
||||||
|
if (isFirmwareUpdateSuccessful(nextStatus)) {
|
||||||
|
Taro.showToast({ title: '系统更新完成', icon: 'success' })
|
||||||
|
} else if (nextStatus === 'failed') {
|
||||||
|
Taro.showToast({ title: '系统更新失败,可重试', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[manage] firmware poll failed:', error)
|
||||||
|
if (firmwareStatusPollDeviceIdRef.current === normalizedDeviceId) {
|
||||||
|
scheduleFirmwareStatusPoll(normalizedDeviceId)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isFirmwareStatusPollingRef.current = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadFirmwareStatus = async (deviceId?: string | null) => {
|
const loadFirmwareStatus = async (deviceId?: string | null) => {
|
||||||
const normalizedDeviceId = String(deviceId || '').trim()
|
const normalizedDeviceId = String(deviceId || '').trim()
|
||||||
if (!normalizedDeviceId) {
|
if (!normalizedDeviceId) {
|
||||||
setFirmwareStatus(null)
|
setFirmwareStatus(null)
|
||||||
setIsLoadingFirmware(false)
|
setIsLoadingFirmware(false)
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,9 +265,13 @@ export default function Sleep() {
|
|||||||
try {
|
try {
|
||||||
const nextFirmwareStatus = await getDeviceFirmwareStatus(normalizedDeviceId)
|
const nextFirmwareStatus = await getDeviceFirmwareStatus(normalizedDeviceId)
|
||||||
setFirmwareStatus(nextFirmwareStatus)
|
setFirmwareStatus(nextFirmwareStatus)
|
||||||
|
syncFirmwareStatusPolling(normalizedDeviceId, nextFirmwareStatus)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('[manage] firmware load failed:', error)
|
console.error('[manage] firmware load failed:', error)
|
||||||
setFirmwareStatus(null)
|
setFirmwareStatus(null)
|
||||||
|
if (firmwareStatusPollDeviceIdRef.current === normalizedDeviceId) {
|
||||||
|
stopFirmwareStatusPolling()
|
||||||
|
}
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: error?.message || '系统更新状态加载失败',
|
title: error?.message || '系统更新状态加载失败',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
@@ -551,7 +663,15 @@ export default function Sleep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleStartFirmwareUpdate = () => {
|
const handleStartFirmwareUpdate = () => {
|
||||||
if (!binding?.device_id || !firmwareStatus?.can_update || isLoadingFirmware || isUpdatingFirmware) return
|
if (
|
||||||
|
!binding?.device_id ||
|
||||||
|
!firmwareStatus?.can_update ||
|
||||||
|
isLoadingFirmware ||
|
||||||
|
isUpdatingFirmware ||
|
||||||
|
isFirmwareUpdateInProgress(firmwareStatus.update_status)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
title: '系统更新',
|
title: '系统更新',
|
||||||
@@ -574,6 +694,7 @@ export default function Sleep() {
|
|||||||
|
|
||||||
const nextFirmwareStatus = await startDeviceFirmwareUpdate(binding.device_id)
|
const nextFirmwareStatus = await startDeviceFirmwareUpdate(binding.device_id)
|
||||||
setFirmwareStatus(nextFirmwareStatus)
|
setFirmwareStatus(nextFirmwareStatus)
|
||||||
|
syncFirmwareStatusPolling(binding.device_id, nextFirmwareStatus)
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '更新指令已发送',
|
title: '更新指令已发送',
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
@@ -787,8 +908,25 @@ export default function Sleep() {
|
|||||||
familyInfo?.device_id === binding.device_id &&
|
familyInfo?.device_id === binding.device_id &&
|
||||||
isFamilyOwner
|
isFamilyOwner
|
||||||
)
|
)
|
||||||
const canUpdateFirmware = Boolean(binding?.device_id && firmwareStatus?.can_update && !isLoadingFirmware && !isUpdatingFirmware)
|
const isFirmwareUpdatingByStatus = isFirmwareUpdateInProgress(firmwareStatus?.update_status)
|
||||||
const firmwareActionText = isLoadingFirmware ? '查询中' : isUpdatingFirmware ? '发送中' : '更新'
|
const canUpdateFirmware = Boolean(
|
||||||
|
binding?.device_id &&
|
||||||
|
firmwareStatus?.can_update &&
|
||||||
|
!isLoadingFirmware &&
|
||||||
|
!isUpdatingFirmware &&
|
||||||
|
!isFirmwareUpdatingByStatus
|
||||||
|
)
|
||||||
|
const firmwareActionText = !binding?.device_id
|
||||||
|
? '更新'
|
||||||
|
: isLoadingFirmware
|
||||||
|
? '查询中'
|
||||||
|
: isUpdatingFirmware
|
||||||
|
? '发送中'
|
||||||
|
: isFirmwareUpdatingByStatus
|
||||||
|
? '更新中'
|
||||||
|
: firmwareStatus?.update_available
|
||||||
|
? '更新'
|
||||||
|
: '已最新'
|
||||||
const roleValue = !binding?.device_id
|
const roleValue = !binding?.device_id
|
||||||
? '未绑定'
|
? '未绑定'
|
||||||
: isLoadingRoles
|
: isLoadingRoles
|
||||||
@@ -803,9 +941,13 @@ export default function Sleep() {
|
|||||||
? '绑定设备后可查看系统版本'
|
? '绑定设备后可查看系统版本'
|
||||||
: isLoadingFirmware
|
: isLoadingFirmware
|
||||||
? '正在查询当前设备版本'
|
? '正在查询当前设备版本'
|
||||||
: firmwareStatus?.update_available
|
: isFirmwareUpdatingByStatus
|
||||||
? '发现可更新版本'
|
? '设备正在更新,请稍候'
|
||||||
: '当前设备版本状态'
|
: normalizeFirmwareUpdateStatus(firmwareStatus?.update_status) === 'failed'
|
||||||
|
? '更新失败,可重试'
|
||||||
|
: firmwareStatus?.update_available
|
||||||
|
? '发现可更新版本'
|
||||||
|
: '当前设备版本状态'
|
||||||
const menuItems: MenuItem[] = [
|
const menuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
icon: require('../../assets/tab-icons/orange-robot.png'),
|
icon: require('../../assets/tab-icons/orange-robot.png'),
|
||||||
|
|||||||
Reference in New Issue
Block a user