mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-11-08 14:38:36 +01:00
1 parent
c23982c339
commit
4d96822c0c
3 files changed
+148
-8
No files matched your search
@@ -3032,6 +3032,20 @@ public class Kst4ContestApplication implements StatusUpdateListener, SettingsHos
|
||||
private java.util.function.Consumer<java.util.List<ChatMessage>> composeChatListener;
|
||||
private java.util.function.Consumer<java.util.List<ClusterMessage>> composeClusterListener;
|
||||
|
||||
/**
|
||||
* Fills the three global Compose message tables from their filtered
|
||||
* controller views.
|
||||
*/
|
||||
/* package */ static void refreshComposeMessageTables(
|
||||
final ChatController controller,
|
||||
final DataTableState<ChatMessage> directed,
|
||||
final DataTableState<ChatMessage> publicMessages,
|
||||
final DataTableState<ChatMessage> qsoOfTheOther) {
|
||||
directed.replaceRows(controller.toMeMessages());
|
||||
publicMessages.replaceRows(controller.toAllMessages());
|
||||
qsoOfTheOther.replaceRows(controller.toOtherMessages());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Compose main window beside the JavaFX one.
|
||||
*
|
||||
@@ -3342,11 +3356,11 @@ public class Kst4ContestApplication implements StatusUpdateListener, SettingsHos
|
||||
return kotlin.Unit.INSTANCE;
|
||||
});
|
||||
|
||||
composeChatListener = rows -> uiDispatcher.runOnUi(() -> {
|
||||
directed.replaceRows(chatcontroller.toMeMessages());
|
||||
publicMessages.replaceRows(rows);
|
||||
qsoOfTheOther.replaceRows(chatcontroller.toOtherMessages());
|
||||
});
|
||||
composeChatListener = rows -> uiDispatcher.runOnUi(() -> refreshComposeMessageTables(
|
||||
chatcontroller,
|
||||
directed,
|
||||
publicMessages,
|
||||
qsoOfTheOther));
|
||||
chatcontroller.getLst_globalChatMessageList().addListener(composeChatListener);
|
||||
|
||||
composeClusterListener = rows -> uiDispatcher.runOnUi(() -> cluster.replaceRows(rows));
|
||||
@@ -3361,10 +3375,8 @@ public class Kst4ContestApplication implements StatusUpdateListener, SettingsHos
|
||||
stations.replaceRows(initialMembers);
|
||||
newJoined.updateActiveMembers(initialMembers);
|
||||
newJoined.updateEvents(chatcontroller.getRecentChatJoins().snapshot());
|
||||
directed.replaceRows(chatcontroller.toMeMessages());
|
||||
publicMessages.replaceRows(chatcontroller.getLst_globalChatMessageList().snapshot());
|
||||
refreshComposeMessageTables(chatcontroller, directed, publicMessages, qsoOfTheOther);
|
||||
cluster.replaceRows(chatcontroller.getLst_clusterMemberList().snapshot());
|
||||
qsoOfTheOther.replaceRows(chatcontroller.toOtherMessages());
|
||||
/*
|
||||
* The only push that does not check the feed for null, and deliberately so: the
|
||||
* feed is assigned earlier in this same method, so a guard here could never fire
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package kst4contest.view
|
||||
|
||||
import kst4contest.controller.ChatController
|
||||
import kst4contest.model.ChatMessage
|
||||
import kst4contest.view.compose.DataTableState
|
||||
import kst4contest.view.compose.RowKeys
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.`when`
|
||||
|
||||
class ComposeMessageTableRefreshTest {
|
||||
|
||||
@Test
|
||||
fun `refresh keeps public table on the filtered controller view`() {
|
||||
val controller = mock(ChatController::class.java)
|
||||
val privateToMe = message("private to me")
|
||||
val publicInitial = message("public initial")
|
||||
val betweenOthers = message("between others")
|
||||
val directed = table()
|
||||
val publicMessages = table()
|
||||
val qsoOfTheOther = table()
|
||||
|
||||
`when`(controller.toMeMessages()).thenReturn(listOf(privateToMe))
|
||||
`when`(controller.toAllMessages()).thenReturn(listOf(publicInitial))
|
||||
`when`(controller.toOtherMessages()).thenReturn(listOf(betweenOthers))
|
||||
|
||||
Kst4ContestApplication.refreshComposeMessageTables(
|
||||
controller,
|
||||
directed,
|
||||
publicMessages,
|
||||
qsoOfTheOther,
|
||||
)
|
||||
|
||||
assertEquals(listOf(privateToMe), directed.rows)
|
||||
assertEquals(listOf(publicInitial), publicMessages.rows)
|
||||
assertEquals(listOf(betweenOthers), qsoOfTheOther.rows)
|
||||
|
||||
val publicLater = message("public later")
|
||||
`when`(controller.toAllMessages()).thenReturn(listOf(publicLater, publicInitial))
|
||||
|
||||
Kst4ContestApplication.refreshComposeMessageTables(
|
||||
controller,
|
||||
directed,
|
||||
publicMessages,
|
||||
qsoOfTheOther,
|
||||
)
|
||||
|
||||
assertEquals(listOf(publicLater, publicInitial), publicMessages.rows)
|
||||
}
|
||||
|
||||
private fun table() = DataTableState(
|
||||
columns = emptyList(),
|
||||
rowKey = RowKeys.byReference<ChatMessage>(),
|
||||
)
|
||||
|
||||
private fun message(text: String) = ChatMessage().apply {
|
||||
messageText = text
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
# Public Message Tab Filter Fix Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make the main-window **Public messages** tab show only ON4KST messages whose protocol receiver is public (`0`, represented as `ALL`).
|
||||
|
||||
**Architecture:** Keep the global message roster as the complete event store and reuse `ChatController.toAllMessages()` as the single existing public-message view. A small package-visible refresh method updates all three Compose message tables so initial population and live updates use the same filtered sources.
|
||||
|
||||
**Tech Stack:** Java 21, Kotlin 2.2, Compose Multiplatform, JUnit 5, Mockito, Gradle
|
||||
|
||||
**Spec:** `github_docs/en-User-Interface.md` and `github_docs/de-Benutzeroberflaeche.md` document the intended global-tab semantics.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Preserve ON4KST parsing, the complete global message history, message order and chat-category identity.
|
||||
- Preserve the selected-station message modes and the existing `pm to me`/`pm to other` semantics.
|
||||
- Do not stage, commit, push or publish without separate authorization.
|
||||
- Source comments remain English.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- A genuine public frame remains in the public tab after initial population and a later refresh.
|
||||
- A PM to the local callsign never enters the public tab.
|
||||
- A directed message between other stations never enters the public tab.
|
||||
- Directed and QSO-of-the-other tables continue to receive their existing filtered views.
|
||||
- Repeated table refreshes replace rows without changing their newest-first order.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Route the Compose public table through the existing filtered view
|
||||
|
||||
**Files:**
|
||||
- Modify: `app-desktop/src/main/java/kst4contest/view/Kst4ContestApplication.java`
|
||||
- Create: `app-desktop/src/test/kotlin/kst4contest/view/ComposeMessageTableRefreshTest.kt`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `ChatController.toMeMessages()`, `ChatController.toAllMessages()`, and `ChatController.toOtherMessages()`
|
||||
- Produces: package-visible `Kst4ContestApplication.refreshComposeMessageTables(...)`
|
||||
|
||||
- [x] **Step 1: Write the failing regression test**
|
||||
|
||||
Create a mixed-message test whose mocked controller exposes separate directed, public and other-station views. Assert that the public table receives only `toAllMessages()`, then change the mocked public result, refresh again and assert the replacement remains filtered.
|
||||
|
||||
- [x] **Step 2: Run the focused test and verify it fails**
|
||||
|
||||
Run: `./gradlew.bat :app-desktop:test --tests kst4contest.view.ComposeMessageTableRefreshTest --console=plain`
|
||||
|
||||
Expected: compilation failure because `refreshComposeMessageTables(...)` does not yet exist.
|
||||
|
||||
- [x] **Step 3: Implement the shared refresh method**
|
||||
|
||||
Add package-visible static `refreshComposeMessageTables(ChatController, DataTableState<ChatMessage>, DataTableState<ChatMessage>, DataTableState<ChatMessage>)`. It must populate the tables from `toMeMessages()`, `toAllMessages()` and `toOtherMessages()` respectively.
|
||||
|
||||
- [x] **Step 4: Use the method for both update paths**
|
||||
|
||||
Call the shared method from the global-message listener on the UI dispatcher and from the initial Compose table population. Remove both raw writes to the public table.
|
||||
|
||||
- [x] **Step 5: Run focused and related tests**
|
||||
|
||||
Run the new test plus `MessageTabsScreenTest`, `MessageBusManagementThreadChatFrameTest`, `SelectedStationMessageFlowTest`, and `SelectedStationMessagePredicateTest`.
|
||||
|
||||
- [x] **Step 6: Run full verification**
|
||||
|
||||
Run `./gradlew.bat build --console=plain`, inspect test-result XML and changed-file static-analysis reports, then run `git diff --check` and `git status --short --branch`.
|
||||
|
||||
- [x] **Step 7: Assess documentation impact**
|
||||
|
||||
Confirm the German and English manuals already state that the global public tab contains public messages only. Update no documentation unless implementation reveals a conflicting invariant.
|
||||
Reference in new issue
Block a user