From 227d270505459e929bbe779dc803649a7b4c84a8 Mon Sep 17 00:00:00 2001 From: kris Date: Sat, 28 Mar 2026 07:18:58 +0800 Subject: [PATCH] feat: add native chat forward selection state --- .../com/hyzq/boss/ProjectChatUiState.java | 34 +++++++++++++++++++ .../com/hyzq/boss/ProjectChatUiStateTest.java | 25 ++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java b/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java index 47e9d1d..53bacfe 100644 --- a/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java +++ b/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java @@ -1,8 +1,23 @@ package com.hyzq.boss; +import androidx.annotation.Nullable; + +import java.util.LinkedHashSet; +import java.util.Set; + public final class ProjectChatUiState { private ProjectChatUiState() {} + public static final class SelectionState { + public final boolean multiSelecting; + public final Set selectedMessageIds; + + public SelectionState(boolean multiSelecting, Set selectedMessageIds) { + this.multiSelecting = multiSelecting; + this.selectedMessageIds = new LinkedHashSet<>(selectedMessageIds); + } + } + public static boolean canSend(String text, boolean sending) { return !sending && text != null && !text.trim().isEmpty(); } @@ -10,4 +25,23 @@ public final class ProjectChatUiState { public static boolean shouldAutoScroll(boolean nearBottom, boolean forced) { return nearBottom || forced; } + + public static SelectionState emptySelection() { + return new SelectionState(false, new LinkedHashSet<>()); + } + + public static SelectionState toggleSelection(@Nullable SelectionState current, String messageId) { + SelectionState state = current == null ? emptySelection() : current; + LinkedHashSet selectedMessageIds = new LinkedHashSet<>(state.selectedMessageIds); + if (selectedMessageIds.contains(messageId)) { + selectedMessageIds.remove(messageId); + return new SelectionState(!selectedMessageIds.isEmpty(), selectedMessageIds); + } + selectedMessageIds.add(messageId); + return new SelectionState(true, selectedMessageIds); + } + + public static boolean canForwardSelection(@Nullable SelectionState state) { + return state != null && state.selectedMessageIds.size() >= 2; + } } diff --git a/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java b/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java index e5bb949..e1e89ee 100644 --- a/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java +++ b/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java @@ -1,6 +1,7 @@ package com.hyzq.boss; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.Test; @@ -20,4 +21,28 @@ public class ProjectChatUiStateTest { assertTrue(ProjectChatUiState.shouldAutoScroll(false, true)); assertFalse(ProjectChatUiState.shouldAutoScroll(false, false)); } + + @Test + public void entersMultiSelectModeAfterFirstToggle() { + ProjectChatUiState.SelectionState state = ProjectChatUiState.toggleSelection(null, "m1"); + assertTrue(state.multiSelecting); + assertEquals(1, state.selectedMessageIds.size()); + assertTrue(state.selectedMessageIds.contains("m1")); + } + + @Test + public void deselectingLastMessageExitsMultiSelectMode() { + ProjectChatUiState.SelectionState state = + new ProjectChatUiState.SelectionState(true, java.util.Set.of("m1")); + ProjectChatUiState.SelectionState next = ProjectChatUiState.toggleSelection(state, "m1"); + assertFalse(next.multiSelecting); + assertTrue(next.selectedMessageIds.isEmpty()); + } + + @Test + public void bundleForwardRequiresAtLeastTwoMessages() { + ProjectChatUiState.SelectionState state = + new ProjectChatUiState.SelectionState(true, java.util.Set.of("m1")); + assertFalse(ProjectChatUiState.canForwardSelection(state)); + } }