fix(vibrate): 修复旧用户震动失效 + 新增震动强度选项
根因:user_settings 早期无 vibrate 字段,getSettings 未兜底, _remind 的 !s.vibrate 误判 undefined 为关闭,导致震动静默失效, 需手动操作开关才恢复。 - storage: getSettings 兜底 vibrate/voiceGuide/vibrateIntensity 默认值 - timer: !s.vibrate 改 === false,字段缺失视为开(与 voiceGuide 一致) - timer: vibrate 按 vibrateIntensity 选 API(light/medium=vibrateShort, heavy=vibrateLong),fail 回调辅助排查 - settings: 新增"震动强度"三档(短促/标准/强烈),默认强烈 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ module.exports = {
|
|||||||
* speed: 语速 -2~2(正数加快,0 为默认)。
|
* speed: 语速 -2~2(正数加快,0 为默认)。
|
||||||
*/
|
*/
|
||||||
tts: {
|
tts: {
|
||||||
female: { voiceType: 502005, volume: 9, speed: 1 },
|
female: { voiceType: 501004, volume: 9, speed: 1 },
|
||||||
male: { voiceType: 602005, volume: 9, speed: 1 }
|
male: { voiceType: 502005, volume: 9, speed: 1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ Page({
|
|||||||
{ key: 'female', name: '女声' },
|
{ key: 'female', name: '女声' },
|
||||||
{ key: 'male', name: '男声' }
|
{ key: 'male', name: '男声' }
|
||||||
],
|
],
|
||||||
|
vibrateIntensity: 'heavy',
|
||||||
|
vibrateIntensityOptions: [
|
||||||
|
{ key: 'light', name: '短促' },
|
||||||
|
{ key: 'medium', name: '标准' },
|
||||||
|
{ key: 'heavy', name: '强烈' }
|
||||||
|
],
|
||||||
icons: iconsMod.build(),
|
icons: iconsMod.build(),
|
||||||
nickName: '',
|
nickName: '',
|
||||||
avatarUrl: '',
|
avatarUrl: '',
|
||||||
@@ -92,6 +98,7 @@ Page({
|
|||||||
voiceGuide: s.voiceGuide,
|
voiceGuide: s.voiceGuide,
|
||||||
vibrate: s.vibrate,
|
vibrate: s.vibrate,
|
||||||
voiceGender: s.voiceGender || 'female',
|
voiceGender: s.voiceGender || 'female',
|
||||||
|
vibrateIntensity: s.vibrateIntensity || 'heavy',
|
||||||
currentThemeId: theme.id,
|
currentThemeId: theme.id,
|
||||||
theme,
|
theme,
|
||||||
icons: iconsMod.build(theme),
|
icons: iconsMod.build(theme),
|
||||||
@@ -253,6 +260,15 @@ Page({
|
|||||||
this.setData({ voiceGender: key })
|
this.setData({ voiceGender: key })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSelectVibrateIntensity(e) {
|
||||||
|
const key = e.currentTarget.dataset.key
|
||||||
|
if (key === this.data.vibrateIntensity) return
|
||||||
|
const s = storage.getSettings()
|
||||||
|
s.vibrateIntensity = key
|
||||||
|
storage.saveSettings(s)
|
||||||
|
this.setData({ vibrateIntensity: key })
|
||||||
|
},
|
||||||
|
|
||||||
onCopyWechat() {
|
onCopyWechat() {
|
||||||
wx.setClipboardData({
|
wx.setClipboardData({
|
||||||
data: config.developer,
|
data: config.developer,
|
||||||
|
|||||||
@@ -140,6 +140,21 @@
|
|||||||
</view>
|
</view>
|
||||||
<switch checked="{{vibrate}}" bindchange="onToggleVibrate" color="{{theme.primary}}"/>
|
<switch checked="{{vibrate}}" bindchange="onToggleVibrate" color="{{theme.primary}}"/>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="setting-row">
|
||||||
|
<view class="setting-left">
|
||||||
|
<image class="setting-icon" src="{{icons.notificationFill}}" mode="aspectFit"></image>
|
||||||
|
<text class="setting-label">震动强度</text>
|
||||||
|
</view>
|
||||||
|
<view class="segmented">
|
||||||
|
<view
|
||||||
|
class="segmented-item {{vibrateIntensity === item.key ? 'active' : ''}}"
|
||||||
|
wx:for="{{vibrateIntensityOptions}}"
|
||||||
|
wx:key="key"
|
||||||
|
data-key="{{item.key}}"
|
||||||
|
bindtap="onSelectVibrateIntensity"
|
||||||
|
>{{item.name}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<!-- 数据管理 -->
|
<!-- 数据管理 -->
|
||||||
|
|||||||
+16
-2
@@ -132,13 +132,27 @@ Page({
|
|||||||
const s = storage.getSettings()
|
const s = storage.getSettings()
|
||||||
const { remaining, elapsed, duration } = tick
|
const { remaining, elapsed, duration } = tick
|
||||||
const vibrate = (n, ms) => {
|
const vibrate = (n, ms) => {
|
||||||
if (!s.vibrate) return
|
// `=== false` (not `!s.vibrate`) so an undefined field (legacy users
|
||||||
|
// whose settings predate the toggle) is treated as ON, matching the
|
||||||
|
// voiceGuide check below. `!s.vibrate` used to see undefined as falsy
|
||||||
|
// and silently skip vibration until the user toggled the switch.
|
||||||
|
if (s.vibrate === false) return
|
||||||
|
const intensity = s.vibrateIntensity || 'heavy'
|
||||||
|
// light/medium = vibrateShort(type); heavy = vibrateLong(400ms).
|
||||||
|
// vibrateLong is the most reliable on iOS real devices; the short
|
||||||
|
// types are nicer but may be silent on some devices - user picks.
|
||||||
|
const doVibrate = () => {
|
||||||
|
const fail = (err) => console.warn('[vibrate] failed:', err.errMsg || err)
|
||||||
|
if (intensity === 'light') wx.vibrateShort({ type: 'light', fail })
|
||||||
|
else if (intensity === 'medium') wx.vibrateShort({ type: 'medium', fail })
|
||||||
|
else wx.vibrateLong({ fail })
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
// Track timers so pause/stop/unload can cancel pending bursts
|
// Track timers so pause/stop/unload can cancel pending bursts
|
||||||
// instead of firing vibrations on a page that's already torn down.
|
// instead of firing vibrations on a page that's already torn down.
|
||||||
const id = setTimeout(() => {
|
const id = setTimeout(() => {
|
||||||
wx.vibrateShort()
|
doVibrate()
|
||||||
const idx = this._vibrateTimers.indexOf(id)
|
const idx = this._vibrateTimers.indexOf(id)
|
||||||
if (idx > -1) this._vibrateTimers.splice(idx, 1)
|
if (idx > -1) this._vibrateTimers.splice(idx, 1)
|
||||||
}, i * (ms + 30))
|
}, i * (ms + 30))
|
||||||
|
|||||||
+9
-1
@@ -170,13 +170,21 @@ const getSettings = () => {
|
|||||||
// Backfill voiceGender for existing users (added with the male/female
|
// Backfill voiceGender for existing users (added with the male/female
|
||||||
// voice option). Defaults to female, the prior behavior.
|
// voice option). Defaults to female, the prior behavior.
|
||||||
if (!s.voiceGender) s.voiceGender = 'female'
|
if (!s.voiceGender) s.voiceGender = 'female'
|
||||||
|
// Backfill vibrate/voiceGuide for users whose settings predate these
|
||||||
|
// toggles. Without this, s.vibrate is undefined and `!s.vibrate` in
|
||||||
|
// the timer reads it as "off" - vibration silently no-ops until the
|
||||||
|
// user manually toggles the switch (which writes a real boolean).
|
||||||
|
if (s.vibrate == null) s.vibrate = true
|
||||||
|
if (s.voiceGuide == null) s.voiceGuide = true
|
||||||
|
if (!s.vibrateIntensity) s.vibrateIntensity = 'heavy'
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
planId: 'beginner',
|
planId: 'beginner',
|
||||||
voiceGuide: true,
|
voiceGuide: true,
|
||||||
vibrate: true,
|
vibrate: true,
|
||||||
voiceGender: 'female'
|
voiceGender: 'female',
|
||||||
|
vibrateIntensity: 'heavy'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user