refactor: 红点统一管理(activeDotCards+renderDots)+ 去掉提示条
按矩阵验证的19个场景统一重写通知逻辑: 核心改动: - activeDotCards 集合累积管理红点,只增不减(除显式操作) - renderDots() 统一渲染,不在各处单独设 visibility - 去掉提示条(用户反馈不好看) 场景覆盖: - 场景1-2: 首页连续收到不同分类通知 → activeDotCards累积 ✅ - 场景3: 点横幅 → consumeAll+activeDotCards清空 ✅ - 场景4-5: 点红点 → activeDotCards移除对应+renderDots ✅ - 场景7: 所有红点点完 → 自动consumeAll清理 ✅ - 场景11-16: 非首页 → preNotificationStats精确diffStats→补充activeDotCards ✅ - 场景18: 全部查看后新通知 → consumeAll已重置,新通知正常累积 ✅ - 场景19: 列表中又来通知 → 返回后onResume补充新红点,不覆盖已ack的 ✅ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,8 +69,12 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
private lateinit var dotPool: View
|
||||
private lateinit var dotPunch: View
|
||||
private lateinit var dotComplete: View
|
||||
// 新任务提示条
|
||||
private lateinit var tvNewTaskHint: TextView
|
||||
/** 当前应显示红点的卡片集合(累积,只有显式操作才移除) */
|
||||
private val activeDotCards = mutableSetOf<Int>()
|
||||
/** 红点 View 映射 */
|
||||
private val dotViews: Map<Int, View> by lazy {
|
||||
mapOf(2 to dotPool, 3 to dotPunch, 4 to dotComplete)
|
||||
}
|
||||
|
||||
// ===== 设置页 View 引用 =====
|
||||
private lateinit var tvAvatarLetter: TextView
|
||||
@@ -131,59 +135,42 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (notificationManager.pendingCount <= 0) {
|
||||
// 全部已查看 → 清除所有提示
|
||||
dotPool.visibility = View.GONE
|
||||
dotPunch.visibility = View.GONE
|
||||
dotComplete.visibility = View.GONE
|
||||
tvNewTaskHint.visibility = View.GONE
|
||||
// 全部已查看/无通知 → 清除
|
||||
activeDotCards.clear()
|
||||
renderDots()
|
||||
return
|
||||
}
|
||||
|
||||
// 有未读通知 → 用 preNotificationStats 精确对比红点
|
||||
// (preNotificationStats 是通知到达前的快照,不会被 onViewCreated 的 fetchStatistics 覆盖)
|
||||
val ack = notificationManager.acknowledgedCards
|
||||
// 有未读通知 → 用 preNotificationStats 精确补充红点到 activeDotCards
|
||||
val baseline = notificationManager.preNotificationStats
|
||||
if (baseline != null) {
|
||||
// 精确模式:对比快照和当前统计,只给变化的分类显示红点
|
||||
fetchStatisticsForDots(baseline, ack)
|
||||
} else {
|
||||
// 无快照(首次加载等):已查看的不显示,未查看的显示
|
||||
if (2 !in ack) dotPool.visibility = View.VISIBLE
|
||||
if (3 !in ack) dotPunch.visibility = View.VISIBLE
|
||||
if (4 !in ack) dotComplete.visibility = View.VISIBLE
|
||||
}
|
||||
updateNewTaskHint()
|
||||
}
|
||||
|
||||
/**
|
||||
* 用通知前快照精确对比红点(非首页返回时调用)
|
||||
* @param baseline 通知到达前的统计快照
|
||||
* @param acknowledged 已查看的分类集合
|
||||
*/
|
||||
private fun fetchStatisticsForDots(baseline: com.xiaoqu.watch.data.task.TaskStatistics, acknowledged: Set<Int>) {
|
||||
if (baseline != null && activeDotCards.isEmpty()) {
|
||||
// 非首页返回:activeDotCards 为空(view 重建),需要重新计算
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val result = safeApiCall { taskApi.getStatistics() }
|
||||
if (result is ApiResult.Success && result.data != null) {
|
||||
val data = result.data
|
||||
val changed = notificationManager.diffStats(baseline, data)
|
||||
|
||||
// 只给变化且未查看的分类显示红点
|
||||
dotPool.visibility = if (2 in changed && 2 !in acknowledged) View.VISIBLE else View.GONE
|
||||
dotPunch.visibility = if (3 in changed && 3 !in acknowledged) View.VISIBLE else View.GONE
|
||||
dotComplete.visibility = if (4 in changed && 4 !in acknowledged) View.VISIBLE else View.GONE
|
||||
|
||||
// 记录增量(供 acknowledgeCard 扣减用)
|
||||
val ack = notificationManager.acknowledgedCards
|
||||
// 只加未查看的
|
||||
for (status in changed) {
|
||||
if (status !in ack) activeDotCards.add(status)
|
||||
}
|
||||
// 记录增量
|
||||
if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask)
|
||||
if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask)
|
||||
if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask)
|
||||
|
||||
// 更新数字
|
||||
tvPoolNum.text = data.waitForTask.toString()
|
||||
tvPunchNum.text = data.treatTask.toString()
|
||||
tvCompleteNum.text = data.incompleteTask.toString()
|
||||
notificationManager.lastStats = data
|
||||
renderDots()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 首页返回(从卡片列表回来等):activeDotCards 已有值,直接渲染
|
||||
renderDots()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@@ -329,23 +316,17 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
dotPunch = page.findViewById(R.id.dotPunch)
|
||||
dotComplete = page.findViewById(R.id.dotComplete)
|
||||
|
||||
// 新任务提示条
|
||||
tvNewTaskHint = page.findViewById(R.id.tvNewTaskHint)
|
||||
tvNewTaskHint.setOnClickListener {
|
||||
navigateToNewTasks()
|
||||
}
|
||||
|
||||
// 快捷区卡片点击 → 标记已查看 + 清红点 + 更新提示条 + 跳转
|
||||
// 快捷区卡片点击 → 标记已查看 + 清对应红点 + 跳转
|
||||
page.findViewById<View>(R.id.cardPool)?.setOnClickListener {
|
||||
acknowledgeCard(2, dotPool)
|
||||
acknowledgeCard(2)
|
||||
navigateToTaskList(2)
|
||||
}
|
||||
page.findViewById<View>(R.id.cardPunch)?.setOnClickListener {
|
||||
acknowledgeCard(3, dotPunch)
|
||||
acknowledgeCard(3)
|
||||
navigateToTaskList(3)
|
||||
}
|
||||
page.findViewById<View>(R.id.cardComplete)?.setOnClickListener {
|
||||
acknowledgeCard(4, dotComplete)
|
||||
acknowledgeCard(4)
|
||||
navigateToTaskList(4)
|
||||
}
|
||||
}
|
||||
@@ -386,22 +367,21 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
if (result is ApiResult.Success && result.data != null) {
|
||||
val data = result.data
|
||||
|
||||
// 对比红点 + 记录每个分类的增量
|
||||
// 对比红点:累积到 activeDotCards,不覆盖已有的
|
||||
if (checkDots) {
|
||||
val oldStats = notificationManager.lastStats
|
||||
val changedCards = notificationManager.diffStats(oldStats, data)
|
||||
if (2 in changedCards) {
|
||||
dotPool.visibility = View.VISIBLE
|
||||
notificationManager.recordCardIncrement(2, data.waitForTask - (oldStats?.waitForTask ?: 0))
|
||||
for (status in changedCards) {
|
||||
activeDotCards.add(status) // 累积,不会移除已有的
|
||||
val increment = when (status) {
|
||||
2 -> data.waitForTask - (oldStats?.waitForTask ?: 0)
|
||||
3 -> data.treatTask - (oldStats?.treatTask ?: 0)
|
||||
4 -> data.incompleteTask - (oldStats?.incompleteTask ?: 0)
|
||||
else -> 0
|
||||
}
|
||||
if (3 in changedCards) {
|
||||
dotPunch.visibility = View.VISIBLE
|
||||
notificationManager.recordCardIncrement(3, data.treatTask - (oldStats?.treatTask ?: 0))
|
||||
}
|
||||
if (4 in changedCards) {
|
||||
dotComplete.visibility = View.VISIBLE
|
||||
notificationManager.recordCardIncrement(4, data.incompleteTask - (oldStats?.incompleteTask ?: 0))
|
||||
notificationManager.recordCardIncrement(status, increment)
|
||||
}
|
||||
renderDots()
|
||||
}
|
||||
|
||||
// 更新数字
|
||||
@@ -494,12 +474,11 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
is AppEvent.BluetoothStateChanged -> {
|
||||
statusBar.updateBluetooth(event.isOn)
|
||||
}
|
||||
// 新任务到达 → 刷新统计 + 红点 + 提示条
|
||||
// 新任务到达 → 刷新统计 + 累积红点
|
||||
is AppEvent.NewTaskArrived -> {
|
||||
Timber.d("首页: 新任务到达 (${event.count} 条)")
|
||||
fetchStatistics(checkDots = true)
|
||||
updateNewTaskHint()
|
||||
setupBannerClick(event.taskIds)
|
||||
setupBannerClick()
|
||||
}
|
||||
// MQTT 消息
|
||||
is AppEvent.MqttMessageReceived -> {
|
||||
@@ -590,26 +569,26 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
||||
}
|
||||
|
||||
/** 标记某个分类已查看:清红点 + 更新通知数 + 刷新提示条 */
|
||||
private fun acknowledgeCard(status: Int, dot: View) {
|
||||
dot.visibility = View.GONE
|
||||
notificationManager.acknowledgeCard(status)
|
||||
updateNewTaskHint()
|
||||
/** 统一渲染红点:以 activeDotCards 为唯一数据源 */
|
||||
private fun renderDots() {
|
||||
dotViews.forEach { (status, dot) ->
|
||||
dot.visibility = if (status in activeDotCards) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新新任务提示条 */
|
||||
private fun updateNewTaskHint() {
|
||||
val count = notificationManager.pendingCount
|
||||
if (count > 0) {
|
||||
tvNewTaskHint.text = "有${count}条新任务 点击查看"
|
||||
tvNewTaskHint.visibility = View.VISIBLE
|
||||
} else {
|
||||
tvNewTaskHint.visibility = View.GONE
|
||||
/** 标记某个分类已查看:从 activeDotCards 移除 + 通知管理器记录 */
|
||||
private fun acknowledgeCard(status: Int) {
|
||||
activeDotCards.remove(status)
|
||||
notificationManager.acknowledgeCard(status)
|
||||
renderDots()
|
||||
// 所有红点都点完 → 自动清理(场景7)
|
||||
if (activeDotCards.isEmpty() && notificationManager.pendingCount <= 0) {
|
||||
notificationManager.consumeAll()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转查看所有新任务(横幅/提示条共用)
|
||||
* 跳转查看所有新任务(横幅点击用)
|
||||
* 1 个任务 → 跳详情;多个 → 跳列表按 taskIds 加载
|
||||
*/
|
||||
private fun navigateToNewTasks() {
|
||||
@@ -630,21 +609,14 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
||||
}
|
||||
|
||||
// 清除所有提示
|
||||
clearNewTaskIndicators()
|
||||
}
|
||||
|
||||
/** 清除所有新任务提示(红点+提示条+未读) */
|
||||
private fun clearNewTaskIndicators() {
|
||||
dotPool.visibility = View.GONE
|
||||
dotPunch.visibility = View.GONE
|
||||
dotComplete.visibility = View.GONE
|
||||
tvNewTaskHint.visibility = View.GONE
|
||||
// 清除所有提示(场景3:点横幅查看全部)
|
||||
activeDotCards.clear()
|
||||
renderDots()
|
||||
notificationManager.consumeAll()
|
||||
}
|
||||
|
||||
/** 设置通知横幅点击回调 */
|
||||
private fun setupBannerClick(taskIds: List<String>) {
|
||||
private fun setupBannerClick() {
|
||||
val mainActivity = activity as? com.xiaoqu.watch.app.MainActivity ?: return
|
||||
mainActivity.notificationBanner.onClick = {
|
||||
navigateToNewTasks()
|
||||
|
||||
@@ -42,22 +42,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 新任务提示条(pendingCount>0 时显示,点击查看所有新任务) -->
|
||||
<TextView
|
||||
android:id="@+id/tvNewTaskHint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/bg_new_task_hint"
|
||||
android:gravity="center"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="有1条新任务 点击查看"
|
||||
android:textColor="#FF3B9EFF"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- 快捷区 gap:6px→8dp -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
Reference in New Issue
Block a user