Browse Source
真机反馈的三条。 ## 1) 分类筛选点了没反应(我的 bug) 筛选条改的是 categoryFilter,但 eventsForScope **只按日期过滤,从没读过它** —— 值改了没人用。列表源头补上分类条件。 顺带:onDaySelected 里那句 `categoryFilter.value = null` 也去掉了。它是旧首页 折叠态横向条的语义(临时开关,切天清掉合理);拾忆页的筛选条是常驻的, 选了「花销」再点一天就被清空,看起来同样像按钮失灵。 ## 2) 本地未同步项每点必弹,且无从同步 原来只是拦下来提示一句,没给出路,连点几下还叠一串一模一样的条。 - AssistantDirectiveService.syncPending():把所有 synced=false 的本地闹钟补传, 由拾忆页刷新(进页面/下拉/切前台)驱动。这是自愈机制——服务端没部署、断网、 或恰好那一下超时,闹钟都会先落在本地,等能连上时自动补上去,不用用户做任何事。 遇到业务错误或网络异常就 break:后面的一样会失败,别把整串请求都打一遍。 加了重入保护。 - refreshFromServer 里**先补传再拉取**。反过来的话刚补传的这次拉不到, 要等下一次刷新才显示。 - 新增 ToastThrottle:同一 key 3 秒内只弹一次(比 snackbar 默认停留略长, 保证上一条没消失就不会再来一条)。memory 的三处提示都接上。 登出时清节流状态,免得上个账号的记录影响下一个。 - 点未同步项时除了提示,**顺手触发一次补传**——用户点它说明他在意这条, 正好借这个动作试一次同步,成功了下次点就能编辑。 ## 3) EMAI 回复没声音(试修,需真机验证) 日志里 PcmStreamPlayer 反复报 -11850 Operation Stopped,每来一包音频重试一次。 链路:手机这条路是「按住说话」,MicPcmStreamer 用 record 的 echoCancel/noiseSuppress,在 iOS 上插件会把 AVAudioSession 切到 voice-processing 并自己持有;松手时 _mic.stop() 只停引擎、**不还原会话**。随后回复音频到达, AVPlayer 在那个会话上起不来。 _ensureSession 原来只在首次 configure、**从头到尾没有 setActive**。改成: 配置仍只做一次(配置本身是静态的),但**每轮 begin 都显式 setActive(true)**, 把会话按本方配置拿回来;起播失败时复位 _sessionConfigured,下一轮重新走完整 configure+setActive——只补激活救不回被改过 category/mode 的会话。 ⚠️ 没动 category/mode/routing 那几个值。CLAUDE.md 记着这块「被改坏过三次」 (外放/听筒/A2DP 来回绕),这次只加激活时机,不碰路由语义。 ⚠️ 这条是根据日志推断的,**未经真机验证**。 ## 测试 dart analyze 零错误。flutter test 有 7 个失败,逐个核对过是既有失败: 6 个语言的 translations_test 报「与 enUS key 不一致」,实测这些语言相对 enUS 缺 14~188 个 key,**其中属于本次新增的是 0 个**;id_verify_dialog_test 同理。 改动前后失败数都是 7。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>main
6 changed files with 153 additions and 13 deletions
@ -0,0 +1,49 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
/// 轻量提示的节流器。 |
||||
|
/// |
||||
|
/// 解决的是「连续点同一个东西弹一串一模一样的条」:用户点三下列表项, |
||||
|
/// 屏幕上就叠三条相同的提示,把内容全挡住,而且要等它们逐条超时才散。 |
||||
|
/// |
||||
|
/// 规则:同一个 [key] 在 [window] 内只弹一次;不同 key 互不影响。 |
||||
|
/// 已经在显示的同名提示不会被顶掉重来。 |
||||
|
class ToastThrottle { |
||||
|
ToastThrottle._(); |
||||
|
|
||||
|
static final Map<String, DateTime> _last = {}; |
||||
|
|
||||
|
/// 同一提示的最小间隔。取 3 秒:比 snackbar 默认停留时间略长, |
||||
|
/// 保证「上一条还没消失就不会再来一条」。 |
||||
|
static const Duration window = Duration(seconds: 3); |
||||
|
|
||||
|
/// 弹一条受节流的提示。被节流掉时返回 false。 |
||||
|
static bool show( |
||||
|
String key, { |
||||
|
required String title, |
||||
|
required String message, |
||||
|
SnackPosition position = SnackPosition.TOP, |
||||
|
Color? background, |
||||
|
Color? textColor, |
||||
|
}) { |
||||
|
final now = DateTime.now(); |
||||
|
final last = _last[key]; |
||||
|
if (last != null && now.difference(last) < window) return false; |
||||
|
_last[key] = now; |
||||
|
Get.snackbar( |
||||
|
title, |
||||
|
message, |
||||
|
snackPosition: position, |
||||
|
backgroundColor: background, |
||||
|
colorText: textColor, |
||||
|
duration: const Duration(seconds: 2), |
||||
|
); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// 清掉某个 key 的记录,让它下次立刻能弹(状态变化后想立即反馈时用) |
||||
|
static void reset(String key) => _last.remove(key); |
||||
|
|
||||
|
/// 全清。登出、切账号时用,避免上个账号的节流状态影响下一个。 |
||||
|
static void clear() => _last.clear(); |
||||
|
} |
||||
Loading…
Reference in new issue