feat: 更新应用版本号并在认证组件中显示;增强 BLE 连接和错误处理逻辑

This commit is contained in:
康猛 2026-01-08 20:42:34 +08:00
parent e7459cc284
commit 08b8495b3d
9 changed files with 370 additions and 58 deletions

View File

@ -8,6 +8,6 @@ export const ANDROID_ID = 'com.duomi.duooomi'
export const IOS_ID = ANDROID_ID
export const IOS_UNIVERSAL_LINK = 'duooomi.bowong.cn'
export const OWNER_ID = 'x3xbTCWf7dbtWu4gGU2TeI054L77xtkt'
export const APP_VERSION = 'dev202601081220'
export const APP_VERSION = 'dev202601082028'
export const ALIPAY_SCHEMA = 'alipay2021006119657394'
export const ALIPAY_SCHEMA_SANDBOX = '9021000158673972'

View File

@ -518,7 +518,7 @@ type HeroCircleProps = {
onQuickGen: () => void
onOpenSearch: () => void
}
const HeroCircle = memo<HeroCircleProps>(function HeroCircle({ selectedItem, onQuickGen, onOpenSearch }) {
const HeroCircle = observer<HeroCircleProps>(function HeroCircle({ selectedItem, onQuickGen, onOpenSearch }) {
const isAuthenticated = userStore.isAuthenticated
const { balance } = userBalanceStore
const existItem = !!selectedItem?.url

View File

@ -5,6 +5,7 @@ import React, { useCallback, useState } from 'react'
import { TextInput } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
import { APP_VERSION } from '@/app.constants'
import BannerSection from '@/components/BannerSection'
import { setAuthToken, signIn, signUp } from '@/lib/auth'
type AuthMode = 'login' | 'register'
@ -340,6 +341,7 @@ export default function Auth() {
<Block className="mt-[24px] items-center">
<Text className="font-700 text-[12px] text-gray-400">© 2025 LOOMART. All rights reserved.</Text>
<Text className="font-700 text-[12px] text-gray-400">: {APP_VERSION}</Text>
</Block>
</Block>
</Block>

View File

@ -163,30 +163,43 @@ export class BleClient {
if (this.connectedDevice) {
console.log(`Disconnecting from ${this.connectedDevice.id}`)
// ✅ 修复:先清理监听器
this.removeAllListeners()
const deviceId = this.connectedDevice.id
// ✅ 修复:安全断开
await this.connectedDevice.cancelConnection()
// ✅ 修复:先触发断开状态,然后清理
this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTING })
try {
// ✅ 修复:安全断开,设置较短超时
await Promise.race([
this.connectedDevice.cancelConnection(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Disconnect timeout')), 5000)),
])
} catch (disconnectError: any) {
// 忽略断开连接过程中的预期错误
const errorMsg = disconnectError?.message || String(disconnectError)
console.debug('Disconnect operation completed with:', errorMsg)
}
// ✅ 修复:无论断开是否成功,都清理状态
this.connectedDevice = null
this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED })
console.log('Device disconnected successfully')
}
} catch (error: any) {
const errorMsg = error?.message || String(error)
console.error('Disconnect error:', errorMsg)
// ✅ 修复:忽略预期的断开连接错误
if (
errorMsg.includes('already disconnected') ||
errorMsg.includes('GATT_SUCCESS') ||
errorMsg.includes('Disconnected') ||
error.errorCode === 0
) {
console.debug('Disconnect completed (expected error):', errorMsg)
// 即使出现错误,也要清理状态
if (this.connectedDevice) {
const deviceId = this.connectedDevice.id
this.connectedDevice = null
} else {
console.error('Disconnect error:', errorMsg)
throw error
this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED })
}
// 只在严重错误时抛出异常
if (!this.isDisconnectionError(error)) {
throw this.normalizeError(error)
}
}
}
@ -239,18 +252,36 @@ export class BleClient {
listener: (error: BleError | null, value: string | null) => void,
) {
if (!this.manager) {
listener({ message: 'BLE not supported on web', errorCode: 0, reason: null }, null)
listener({ message: 'BLE not supported on web', errorCode: -1, reason: 'platform_not_supported' }, null)
return {
remove: () => {},
}
}
return this.manager.monitorCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID, (error, char) => {
if (error) {
listener(this.normalizeError(error), null)
} else {
listener(null, char?.value || null)
try {
return this.manager.monitorCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID, (error, char) => {
if (error) {
// ✅ 修复:安全处理错误,确保不传递 null/undefined 值
const normalizedError = this.normalizeError(error)
// 特别处理断开连接错误,避免崩溃
if (this.isDisconnectionError(error)) {
console.debug('Device disconnected during monitoring:', normalizedError.message)
}
listener(normalizedError, null)
} else {
listener(null, char?.value || null)
}
})
} catch (error: any) {
// ✅ 修复:捕获监听器设置时的异常
const normalizedError = this.normalizeError(error)
listener(normalizedError, null)
return {
remove: () => {},
}
})
}
}
public async requestMtu(deviceId: string, mtu: number): Promise<number> {
@ -270,11 +301,32 @@ export class BleClient {
}
private normalizeError(e: any): BleError {
// wrapper to convert PlxError to our BleError
// ✅ 修复:安全处理错误对象,确保所有字段都有有效值
const errorCode = typeof e?.errorCode === 'number' ? e.errorCode : -1
const message =
typeof e?.message === 'string' && e.message.trim()
? e.message
: typeof e === 'string' && e.trim()
? e
: 'Unknown BLE error'
const reason = typeof e?.reason === 'string' && e.reason.trim() ? e.reason : 'unknown'
return {
errorCode: e.errorCode || 0,
message: e.message || 'Unknown error',
reason: e.reason,
errorCode,
message,
reason,
}
}
private isDisconnectionError(error: any): boolean {
const message = error?.message || ''
const reason = error?.reason || ''
return (
message.toLowerCase().includes('disconnect') ||
message.toLowerCase().includes('gatt_conn_terminate') ||
reason.toLowerCase().includes('disconnect') ||
error?.errorCode === 19
) // GATT_CONN_TERMINATE_PEER_USER
}
}

View File

@ -51,40 +51,55 @@ export class BleProtocolService {
this.clearFragments(deviceId)
try {
console.log('Initializing BLE protocol service for device:', deviceId)
this.subscription = await this.client.monitor(
deviceId,
BLE_UUIDS.SERVICE,
BLE_UUIDS.READ_CHARACTERISTIC,
(error, value) => {
if (error) {
// Check for known native crash error and ignore/log as debug
if (
error.errorCode === 0 &&
error.message.includes('Unknown error') &&
error.reason?.includes('PromiseImpl.reject')
) {
console.debug('Ignored native monitor error', error)
// ✅ 修复:增强错误处理,避免崩溃
const errorMsg = error.message || 'Unknown monitor error'
// 忽略预期的断开连接错误
if (this.isExpectedDisconnectionError(error)) {
console.debug('Device disconnected during monitoring:', errorMsg)
return
}
console.warn('Monitor error', error)
// 忽略已知的原生崩溃错误
if (this.isKnownNativeCrashError(error)) {
console.debug('Ignored known native monitor error:', errorMsg)
return
}
console.warn('BLE monitor error:', errorMsg)
return
}
if (value) {
const buffer = Buffer.from(value, 'base64')
const hexString =
buffer
.toString('hex')
.match(/.{1,2}/g)
?.join(' ')
.toUpperCase() || ''
console.log(`[BleProtocol] Received ${buffer.byteLength} bytes:`, hexString)
this.handleRawData(deviceId, buffer)
if (value) {
try {
const buffer = Buffer.from(value, 'base64')
const hexString =
buffer
.toString('hex')
.match(/.{1,2}/g)
?.join(' ')
.toUpperCase() || ''
console.log(`[BleProtocol] Received ${buffer.byteLength} bytes:`, hexString)
this.handleRawData(deviceId, buffer)
} catch (parseError: any) {
console.warn('Failed to parse received data:', parseError.message)
}
}
},
)
} catch (error) {
console.error('Failed to initialize protocol service:', error)
console.log('BLE protocol service initialized successfully')
} catch (error: any) {
console.error('Failed to initialize protocol service:', error.message)
throw error
}
}
@ -92,12 +107,22 @@ export class BleProtocolService {
public disconnect() {
if (this.subscription) {
try {
console.log('Cleaning up BLE protocol subscription...')
this.subscription.remove()
} catch (e) {
console.warn('Failed to remove subscription', e)
console.log('BLE protocol subscription removed successfully')
} catch (e: any) {
// ✅ 修复:安全处理订阅清理错误,避免崩溃
const errorMsg = e?.message || String(e)
console.debug('Subscription cleanup completed with warning:', errorMsg)
} finally {
// ✅ 确保订阅引用被清理
this.subscription = null
}
this.subscription = null
}
// ✅ 清理分片缓存
this.fragments.clear()
console.log('BLE protocol service disconnected')
}
private handleRawData(deviceId: string, data: Buffer) {
@ -209,4 +234,28 @@ export class BleProtocolService {
// console.debug("Wrote frame", result);
}
}
private isExpectedDisconnectionError(error: any): boolean {
const message = error?.message?.toLowerCase() || ''
const reason = error?.reason?.toLowerCase() || ''
return (
message.includes('disconnect') ||
message.includes('gatt_conn_terminate') ||
message.includes('connection terminated') ||
reason.includes('disconnect') ||
error?.errorCode === 19
) // GATT_CONN_TERMINATE_PEER_USER
}
private isKnownNativeCrashError(error: any): boolean {
const message = error?.message || ''
const reason = error?.reason || ''
return (
(error.errorCode === 0 && message.includes('Unknown error') && reason?.includes('PromiseImpl.reject')) ||
message.includes('Parameter specified as non-null is null') ||
reason?.includes('SafePromise.reject')
)
}
}

View File

@ -105,6 +105,7 @@
"eslint-plugin-tailwindcss": "^3.18.0",
"eslint-plugin-unicorn": "^59.0.1",
"eslint-plugin-unused-imports": "^4.1.4",
"patch-package": "^8.0.1",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"tailwindcss": "3.4.4",
@ -777,6 +778,8 @@
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
"@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
@ -1271,6 +1274,8 @@
"find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="],
"find-yarn-workspace-root": ["find-yarn-workspace-root@2.0.0", "", { "dependencies": { "micromatch": "^4.0.2" } }, "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
@ -1285,6 +1290,8 @@
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
"fs-readdir-recursive": ["fs-readdir-recursive@1.1.0", "", {}, "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
@ -1459,7 +1466,7 @@
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@ -1509,16 +1516,24 @@
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
"jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="],
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="],
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="],
@ -1735,6 +1750,8 @@
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
"patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
@ -2081,6 +2098,8 @@
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
"tmpl": ["tmpl@1.0.5", "", {}, "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@ -2133,6 +2152,8 @@
"unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="],
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
@ -2463,6 +2484,10 @@
"ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"patch-package/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
"patch-package/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
@ -2505,6 +2530,8 @@
"read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
@ -2513,10 +2540,6 @@
"requireg/resolve": ["resolve@1.7.1", "", { "dependencies": { "path-parse": "^1.0.5" } }, "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw=="],
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="],
@ -2545,8 +2568,6 @@
"whatwg-url-without-unicode/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],

View File

@ -5,6 +5,7 @@
"packageManager": "bun@1.3.5",
"private": true,
"scripts": {
"postinstall": "patch-package",
"claude": "claude --dangerously-skip-permissions",
"start": "dotenv -e .env.development -- expo start",
"start:test": "dotenv -e .env.test -- expo start",
@ -132,6 +133,7 @@
"eslint-plugin-tailwindcss": "^3.18.0",
"eslint-plugin-unicorn": "^59.0.1",
"eslint-plugin-unused-imports": "^4.1.4",
"patch-package": "^8.0.1",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"tailwindcss": "3.4.4",

View File

@ -0,0 +1,157 @@
diff --git a/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java b/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java
index 1234567..abcdefg 100644
--- a/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java
+++ b/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java
@@ -168,7 +168,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -187,7 +187,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -318,7 +318,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -338,7 +338,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -358,7 +358,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -422,7 +422,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -442,7 +442,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -483,7 +483,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -627,7 +627,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -654,7 +654,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -680,7 +680,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
});
}
@@ -706,7 +706,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -732,7 +732,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -757,7 +757,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -788,7 +788,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -818,7 +818,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);
@@ -848,7 +848,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule {
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);

29
scripts/patch-ble-plx.js Normal file
View File

@ -0,0 +1,29 @@
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, '..', 'node_modules', 'react-native-ble-plx', 'android', 'src', 'main', 'java', 'com', 'bleplx', 'BlePlxModule.java');
console.log('正在修复 react-native-ble-plx 的 Android 崩溃问题...');
try {
let content = fs.readFileSync(filePath, 'utf8');
// 替换所有的 safePromise.reject(null, errorConverter.toJs(error))
const originalPattern = 'safePromise.reject(null, errorConverter.toJs(error));';
const fixedPattern = 'safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));';
const beforeCount = (content.match(/safePromise\.reject\(null, errorConverter\.toJs\(error\)\);/g) || []).length;
content = content.replace(/safePromise\.reject\(null, errorConverter\.toJs\(error\)\);/g, fixedPattern);
const afterCount = (content.match(/safePromise\.reject\(error\.errorCode\.name\(\), errorConverter\.toJs\(error\)\);/g) || []).length;
fs.writeFileSync(filePath, content, 'utf8');
console.log(`✅ 成功修复 ${beforeCount} 个实例`);
console.log(`✅ 验证:发现 ${afterCount} 个修复后的实例`);
console.log('现在请运行: bunx patch-package react-native-ble-plx');
} catch (error) {
console.error('❌ 修复失败:', error.message);
process.exit(1);
}