fix: keep chat scroll usable while sending

This commit is contained in:
kris
2026-04-18 05:23:14 +08:00
parent 449f84fcbc
commit bb237fdd4f
4 changed files with 133 additions and 31 deletions

View File

@@ -45,7 +45,8 @@ public class ProjectDetailActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.reloadCount);
assertEquals(0, activity.reloadCount);
assertEquals(1, activity.messageReloadCount);
}
@Test
@@ -131,7 +132,8 @@ public class ProjectDetailActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(2, activity.reloadCount);
assertEquals(1, activity.reloadCount);
assertEquals(1, activity.messageReloadCount);
}
@Test
@@ -159,6 +161,7 @@ public class ProjectDetailActivityRealtimeTest {
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.reloadCount);
assertEquals(0, activity.messageReloadCount);
}
@Test
@@ -196,7 +199,8 @@ public class ProjectDetailActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.reloadCount);
assertEquals(0, activity.reloadCount);
assertEquals(1, activity.messageReloadCount);
}
@Test
@@ -241,13 +245,15 @@ public class ProjectDetailActivityRealtimeTest {
);
Shadows.shadowOf(activity.getMainLooper()).idle();
assertEquals(1, activity.loadCallCount);
assertEquals(0, activity.loadCallCount);
assertEquals(1, activity.messageLoadCallCount);
assertEquals(0, activity.renderCount);
activity.releaseFirstLoad();
waitFor(() -> activity.renderCount == 2 && activity.loadCallCount == 2);
waitFor(() -> activity.renderCount == 2 && activity.messageLoadCallCount == 1 && activity.loadCallCount == 1);
assertEquals(2, activity.loadCallCount);
assertEquals(1, activity.loadCallCount);
assertEquals(1, activity.messageLoadCallCount);
assertEquals(2, activity.renderCount);
}
@@ -313,7 +319,9 @@ public class ProjectDetailActivityRealtimeTest {
public static class TestRealtimeProjectDetailActivity extends ProjectDetailActivity {
int reloadCount;
int messageReloadCount;
volatile int loadCallCount;
volatile int messageLoadCallCount;
volatile int renderCount;
private CountDownLatch firstLoadStarted;
private CountDownLatch releaseFirstLoad;
@@ -344,6 +352,26 @@ public class ProjectDetailActivityRealtimeTest {
super.reload();
}
@Override
ProjectSnapshot loadProjectMessagesSnapshotForRefresh() throws Exception {
messageReloadCount += 1;
messageLoadCallCount += 1;
if (messageLoadCallCount == 1 && firstLoadStarted != null && releaseFirstLoad != null) {
firstLoadStarted.countDown();
releaseFirstLoad.await(2, TimeUnit.SECONDS);
}
return new ProjectSnapshot(
new JSONObject().put(
"project",
new JSONObject()
.put("name", "北区试产线")
.put("messages", new JSONArray())
),
null,
null
);
}
@Override
ProjectSnapshot loadProjectSnapshotForRefresh() throws Exception {
loadCallCount += 1;

View File

@@ -17,6 +17,7 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.appcompat.app.AlertDialog;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -131,6 +132,22 @@ public class ProjectDetailActivityUiTest {
assertEquals(R.id.project_chat_scroll, ((View) contentLayout.getParent()).getId());
}
@Test
public void chatRefreshUsesScrollViewAsChildScrollReference() {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
TestProjectDetailActivity activity = Robolectric
.buildActivity(TestProjectDetailActivity.class, intent)
.setup()
.get();
SwipeRefreshLayout refreshLayout = activity.findViewById(R.id.screen_refresh_layout);
Object childScrollCallback = ReflectionHelpers.getField(refreshLayout, "mChildScrollUpCallback");
assertNotNull(childScrollCallback);
}
@Test
public void composerRowLayoutChangeWithFocusedInput_scrollsChatToBottomAgain() {
Intent intent = new Intent()
@@ -176,6 +193,48 @@ public class ProjectDetailActivityUiTest {
assertEquals(baselineScrollCount, activity.scrollChatToBottomCount);
}
@Test
public void composerBusyWithoutRefreshSpinnerStillDisablesComposerInputs() {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
TestProjectDetailActivity activity = Robolectric
.buildActivity(TestProjectDetailActivity.class, intent)
.setup()
.get();
ReflectionHelpers.setField(activity, "composerSending", true);
ReflectionHelpers.callInstanceMethod(activity, "updateComposerSendButtonState");
Button sendButton = activity.findViewById(R.id.project_chat_send);
View composerInput = activity.findViewById(R.id.project_chat_input);
View attachView = activity.findViewById(R.id.project_chat_attach);
SwipeRefreshLayout refreshLayout = activity.findViewById(R.id.screen_refresh_layout);
assertFalse(refreshLayout.isRefreshing());
assertFalse(sendButton.isEnabled());
assertFalse(composerInput.isEnabled());
assertFalse(attachView.isEnabled());
}
@Test
public void composerAttachmentUsesIconButtonFrameInsteadOfTextButton() {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
TestProjectDetailActivity activity = Robolectric
.buildActivity(TestProjectDetailActivity.class, intent)
.setup()
.get();
View attachView = activity.findViewById(R.id.project_chat_attach);
assertTrue(attachView instanceof ImageButton);
ViewGroup.LayoutParams params = attachView.getLayoutParams();
assertTrue(params.width >= BossUi.dp(activity, 46));
assertTrue(params.height >= BossUi.dp(activity, 46));
}
@Test
public void manualAnalysisAttachmentShowsActionChip() throws Exception {
Intent intent = new Intent()