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 dotPool: View
|
||||||
private lateinit var dotPunch: View
|
private lateinit var dotPunch: View
|
||||||
private lateinit var dotComplete: 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 引用 =====
|
// ===== 设置页 View 引用 =====
|
||||||
private lateinit var tvAvatarLetter: TextView
|
private lateinit var tvAvatarLetter: TextView
|
||||||
@@ -131,59 +135,42 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (notificationManager.pendingCount <= 0) {
|
if (notificationManager.pendingCount <= 0) {
|
||||||
// 全部已查看 → 清除所有提示
|
// 全部已查看/无通知 → 清除
|
||||||
dotPool.visibility = View.GONE
|
activeDotCards.clear()
|
||||||
dotPunch.visibility = View.GONE
|
renderDots()
|
||||||
dotComplete.visibility = View.GONE
|
|
||||||
tvNewTaskHint.visibility = View.GONE
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有未读通知 → 用 preNotificationStats 精确对比红点
|
// 有未读通知 → 用 preNotificationStats 精确补充红点到 activeDotCards
|
||||||
// (preNotificationStats 是通知到达前的快照,不会被 onViewCreated 的 fetchStatistics 覆盖)
|
|
||||||
val ack = notificationManager.acknowledgedCards
|
|
||||||
val baseline = notificationManager.preNotificationStats
|
val baseline = notificationManager.preNotificationStats
|
||||||
if (baseline != null) {
|
if (baseline != null && activeDotCards.isEmpty()) {
|
||||||
// 精确模式:对比快照和当前统计,只给变化的分类显示红点
|
// 非首页返回:activeDotCards 为空(view 重建),需要重新计算
|
||||||
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>) {
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val result = safeApiCall { taskApi.getStatistics() }
|
val result = safeApiCall { taskApi.getStatistics() }
|
||||||
if (result is ApiResult.Success && result.data != null) {
|
if (result is ApiResult.Success && result.data != null) {
|
||||||
val data = result.data
|
val data = result.data
|
||||||
val changed = notificationManager.diffStats(baseline, data)
|
val changed = notificationManager.diffStats(baseline, data)
|
||||||
|
val ack = notificationManager.acknowledgedCards
|
||||||
// 只给变化且未查看的分类显示红点
|
// 只加未查看的
|
||||||
dotPool.visibility = if (2 in changed && 2 !in acknowledged) View.VISIBLE else View.GONE
|
for (status in changed) {
|
||||||
dotPunch.visibility = if (3 in changed && 3 !in acknowledged) View.VISIBLE else View.GONE
|
if (status !in ack) activeDotCards.add(status)
|
||||||
dotComplete.visibility = if (4 in changed && 4 !in acknowledged) View.VISIBLE else View.GONE
|
}
|
||||||
|
// 记录增量
|
||||||
// 记录增量(供 acknowledgeCard 扣减用)
|
|
||||||
if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask)
|
if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask)
|
||||||
if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask)
|
if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask)
|
||||||
if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask)
|
if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask)
|
||||||
|
|
||||||
// 更新数字
|
// 更新数字
|
||||||
tvPoolNum.text = data.waitForTask.toString()
|
tvPoolNum.text = data.waitForTask.toString()
|
||||||
tvPunchNum.text = data.treatTask.toString()
|
tvPunchNum.text = data.treatTask.toString()
|
||||||
tvCompleteNum.text = data.incompleteTask.toString()
|
tvCompleteNum.text = data.incompleteTask.toString()
|
||||||
notificationManager.lastStats = data
|
notificationManager.lastStats = data
|
||||||
|
renderDots()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 首页返回(从卡片列表回来等):activeDotCards 已有值,直接渲染
|
||||||
|
renderDots()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
@@ -329,23 +316,17 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
dotPunch = page.findViewById(R.id.dotPunch)
|
dotPunch = page.findViewById(R.id.dotPunch)
|
||||||
dotComplete = page.findViewById(R.id.dotComplete)
|
dotComplete = page.findViewById(R.id.dotComplete)
|
||||||
|
|
||||||
// 新任务提示条
|
// 快捷区卡片点击 → 标记已查看 + 清对应红点 + 跳转
|
||||||
tvNewTaskHint = page.findViewById(R.id.tvNewTaskHint)
|
|
||||||
tvNewTaskHint.setOnClickListener {
|
|
||||||
navigateToNewTasks()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 快捷区卡片点击 → 标记已查看 + 清红点 + 更新提示条 + 跳转
|
|
||||||
page.findViewById<View>(R.id.cardPool)?.setOnClickListener {
|
page.findViewById<View>(R.id.cardPool)?.setOnClickListener {
|
||||||
acknowledgeCard(2, dotPool)
|
acknowledgeCard(2)
|
||||||
navigateToTaskList(2)
|
navigateToTaskList(2)
|
||||||
}
|
}
|
||||||
page.findViewById<View>(R.id.cardPunch)?.setOnClickListener {
|
page.findViewById<View>(R.id.cardPunch)?.setOnClickListener {
|
||||||
acknowledgeCard(3, dotPunch)
|
acknowledgeCard(3)
|
||||||
navigateToTaskList(3)
|
navigateToTaskList(3)
|
||||||
}
|
}
|
||||||
page.findViewById<View>(R.id.cardComplete)?.setOnClickListener {
|
page.findViewById<View>(R.id.cardComplete)?.setOnClickListener {
|
||||||
acknowledgeCard(4, dotComplete)
|
acknowledgeCard(4)
|
||||||
navigateToTaskList(4)
|
navigateToTaskList(4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,22 +367,21 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
if (result is ApiResult.Success && result.data != null) {
|
if (result is ApiResult.Success && result.data != null) {
|
||||||
val data = result.data
|
val data = result.data
|
||||||
|
|
||||||
// 对比红点 + 记录每个分类的增量
|
// 对比红点:累积到 activeDotCards,不覆盖已有的
|
||||||
if (checkDots) {
|
if (checkDots) {
|
||||||
val oldStats = notificationManager.lastStats
|
val oldStats = notificationManager.lastStats
|
||||||
val changedCards = notificationManager.diffStats(oldStats, data)
|
val changedCards = notificationManager.diffStats(oldStats, data)
|
||||||
if (2 in changedCards) {
|
for (status in changedCards) {
|
||||||
dotPool.visibility = View.VISIBLE
|
activeDotCards.add(status) // 累积,不会移除已有的
|
||||||
notificationManager.recordCardIncrement(2, data.waitForTask - (oldStats?.waitForTask ?: 0))
|
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) {
|
notificationManager.recordCardIncrement(status, increment)
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
renderDots()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新数字
|
// 更新数字
|
||||||
@@ -494,12 +474,11 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
is AppEvent.BluetoothStateChanged -> {
|
is AppEvent.BluetoothStateChanged -> {
|
||||||
statusBar.updateBluetooth(event.isOn)
|
statusBar.updateBluetooth(event.isOn)
|
||||||
}
|
}
|
||||||
// 新任务到达 → 刷新统计 + 红点 + 提示条
|
// 新任务到达 → 刷新统计 + 累积红点
|
||||||
is AppEvent.NewTaskArrived -> {
|
is AppEvent.NewTaskArrived -> {
|
||||||
Timber.d("首页: 新任务到达 (${event.count} 条)")
|
Timber.d("首页: 新任务到达 (${event.count} 条)")
|
||||||
fetchStatistics(checkDots = true)
|
fetchStatistics(checkDots = true)
|
||||||
updateNewTaskHint()
|
setupBannerClick()
|
||||||
setupBannerClick(event.taskIds)
|
|
||||||
}
|
}
|
||||||
// MQTT 消息
|
// MQTT 消息
|
||||||
is AppEvent.MqttMessageReceived -> {
|
is AppEvent.MqttMessageReceived -> {
|
||||||
@@ -590,26 +569,26 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 标记某个分类已查看:清红点 + 更新通知数 + 刷新提示条 */
|
/** 统一渲染红点:以 activeDotCards 为唯一数据源 */
|
||||||
private fun acknowledgeCard(status: Int, dot: View) {
|
private fun renderDots() {
|
||||||
dot.visibility = View.GONE
|
dotViews.forEach { (status, dot) ->
|
||||||
notificationManager.acknowledgeCard(status)
|
dot.visibility = if (status in activeDotCards) View.VISIBLE else View.GONE
|
||||||
updateNewTaskHint()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新新任务提示条 */
|
/** 标记某个分类已查看:从 activeDotCards 移除 + 通知管理器记录 */
|
||||||
private fun updateNewTaskHint() {
|
private fun acknowledgeCard(status: Int) {
|
||||||
val count = notificationManager.pendingCount
|
activeDotCards.remove(status)
|
||||||
if (count > 0) {
|
notificationManager.acknowledgeCard(status)
|
||||||
tvNewTaskHint.text = "有${count}条新任务 点击查看"
|
renderDots()
|
||||||
tvNewTaskHint.visibility = View.VISIBLE
|
// 所有红点都点完 → 自动清理(场景7)
|
||||||
} else {
|
if (activeDotCards.isEmpty() && notificationManager.pendingCount <= 0) {
|
||||||
tvNewTaskHint.visibility = View.GONE
|
notificationManager.consumeAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转查看所有新任务(横幅/提示条共用)
|
* 跳转查看所有新任务(横幅点击用)
|
||||||
* 1 个任务 → 跳详情;多个 → 跳列表按 taskIds 加载
|
* 1 个任务 → 跳详情;多个 → 跳列表按 taskIds 加载
|
||||||
*/
|
*/
|
||||||
private fun navigateToNewTasks() {
|
private fun navigateToNewTasks() {
|
||||||
@@ -630,21 +609,14 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
findNavController().navigate(R.id.action_home_to_taskList, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除所有提示
|
// 清除所有提示(场景3:点横幅查看全部)
|
||||||
clearNewTaskIndicators()
|
activeDotCards.clear()
|
||||||
}
|
renderDots()
|
||||||
|
|
||||||
/** 清除所有新任务提示(红点+提示条+未读) */
|
|
||||||
private fun clearNewTaskIndicators() {
|
|
||||||
dotPool.visibility = View.GONE
|
|
||||||
dotPunch.visibility = View.GONE
|
|
||||||
dotComplete.visibility = View.GONE
|
|
||||||
tvNewTaskHint.visibility = View.GONE
|
|
||||||
notificationManager.consumeAll()
|
notificationManager.consumeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设置通知横幅点击回调 */
|
/** 设置通知横幅点击回调 */
|
||||||
private fun setupBannerClick(taskIds: List<String>) {
|
private fun setupBannerClick() {
|
||||||
val mainActivity = activity as? com.xiaoqu.watch.app.MainActivity ?: return
|
val mainActivity = activity as? com.xiaoqu.watch.app.MainActivity ?: return
|
||||||
mainActivity.notificationBanner.onClick = {
|
mainActivity.notificationBanner.onClick = {
|
||||||
navigateToNewTasks()
|
navigateToNewTasks()
|
||||||
|
|||||||
@@ -42,22 +42,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</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 -->
|
<!-- 快捷区 gap:6px→8dp -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
Reference in New Issue
Block a user