Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/main/kotlin/chatroom/viewmessages/DisplayableMessages.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
package chatroom.viewmessages

import chatserver.ChatRepositories
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import utils.SmartWrap
import java.lang.System.lineSeparator

class DisplayableMessages(private val repositories: ChatRepositories) {
operator fun invoke(): Flow<String> =
class DisplayableMessages(
private val repositories: ChatRepositories,
private val wrap: SmartWrap,
) {
operator fun invoke(): Flow<List<String>> =
repositories.readMessages().observe()
.filter { it.isOk }
.map { it.item.lastOrNull() }
.filterNotNull()
.mapNotNull { it.item.lastOrNull() }
.map { message ->
val usernames = repositories.readProfiles().latest().takeIf { it.isOk }?.item ?: emptyMap()
val user = usernames[message.userId]?.username ?: "anon"
"$user: ${message.message}"
.removeLineBreaks()
.wrap()
.toList()
}

private fun String.wrap(): String = wrap(this)

companion object {
private fun String.removeLineBreaks(): String = replace("[\r\n]+".toRegex(), " ").trim()

private fun String.toList(): List<String> = split(lineSeparator())
}
}
5 changes: 4 additions & 1 deletion src/main/kotlin/chatroom/viewmessages/ViewMessages.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package chatroom.viewmessages

import chatroom.ChatroomWindow
import chatroom.SEND_MESSAGES_WIDTH
import chatserver.ChatRepositories
import org.koin.dsl.module
import utils.SmartWrap

val viewMessagesModule =
module {
Expand All @@ -16,6 +18,7 @@ val viewMessagesModule =
channel = get<ChatRepositories>().subscribeMessages(),
)
}
scoped { DisplayableMessages(get()) }
scoped { DisplayableMessages(get(), get()) }
scoped { SmartWrap(SEND_MESSAGES_WIDTH) }
}
}
8 changes: 2 additions & 6 deletions src/main/kotlin/chatroom/viewmessages/ViewMessagesPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ package chatroom.viewmessages
import chatroom.BorderedPanel
import chatroom.viewmessages.ViewMessagesViewState.*
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.Border
import com.googlecode.lanterna.gui2.BorderLayout
import com.googlecode.lanterna.gui2.*
import com.googlecode.lanterna.gui2.BorderLayout.Location.CENTER
import com.googlecode.lanterna.gui2.Borders
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.Panel
import view.NonFocusableTextBox

class ViewMessagesPanel(
Expand Down Expand Up @@ -62,7 +58,7 @@ class ViewMessagesPanel(
empty.isVisible = false
messages.isVisible = true
textGUI.guiThread.invokeLater {
messages.addLineAndMaybeScrollDown(state.message)
state.lines.forEach(messages::addLineAndMaybeScrollDown)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ sealed interface ViewMessagesViewState {
const val STATUS = "No Messages Yet"
}

data class Messages(val message: String) : ViewMessagesViewState
data class Messages(val lines: List<String>) : ViewMessagesViewState
}
72 changes: 60 additions & 12 deletions src/test/kotlin/chatroom/viewmessages/DisplayableMessagesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import utils.SmartWrap
import kotlin.time.Duration.Companion.seconds

@OptIn(ExperimentalCoroutinesApi::class)
Expand All @@ -35,53 +36,100 @@ class DisplayableMessagesTest {
every { it.readProfiles() } returns users
every { it.readMessages() } returns messages
}
displayableMessages = DisplayableMessages(repository)
displayableMessages = DisplayableMessages(repository, SmartWrap(50))
}

@Test
fun `when known user sends message, then show username and message`() =
fun `when known user sends message, and message is less than one line long, then show username and message`() =
runTest {
every { users.latest() } returns ProfileResult.ok(mapOf("id" to Profile("id", "Tony KnowsItAll")))
every { messages.observe() } returns flowOf(MessageResult.ok(listOf(Message("id", "I will fail everyone"))))
val emissions = mutableListOf<String>()
val emissions = mutableListOf<List<String>>()
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)
emissions.shouldContainExactly("Tony KnowsItAll: I will fail everyone")
emissions[0].shouldContainExactly("Tony KnowsItAll: I will fail everyone")
}

@Test
fun `when unknown user post message, then show anon message`() =
fun `when known user sends message, and message was sent with many short sentences, then show message split by receiver preferred line length`() =
runTest {
every { users.latest() } returns ProfileResult.ok(mapOf("id" to Profile("id", "Tony KnowsItAll")))
every { messages.observe() } returns
flowOf(
MessageResult.ok(
listOf(
Message(
"id",
"I will\nfail\neveryone\n\n",
),
),
),
)
val emissions = mutableListOf<List<String>>()
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)
emissions[0].shouldContainExactly("Tony KnowsItAll: I will fail everyone")
}

@Test
fun `when known user sends message, and message overflows max line size, then show message split by receiver preference`() =
runTest {
every { users.latest() } returns ProfileResult.ok(mapOf("id" to Profile("id", "Tony KnowsItAll")))
every { messages.observe() } returns
flowOf(
MessageResult.ok(
listOf(
Message(
"id",
"I will fail everyone because I hate all my students",
),
),
),
)
val emissions = mutableListOf<List<String>>()
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)
emissions[0].shouldContainExactly(
"Tony KnowsItAll: I will fail everyone because I ",
"hate all my students",
)
}

@Test
fun `when unknown user post message, and message is less than one line long, then show anon message`() =
runTest {
every { users.latest() } returns ProfileResult.ok(emptyMap())
every { messages.observe() } returns flowOf(MessageResult.ok(listOf(Message("id", "I will fail everyone"))))
val emissions = mutableListOf<String>()
val emissions = mutableListOf<List<String>>()
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)
emissions.shouldContainExactly("anon: I will fail everyone")
emissions[0].shouldContainExactly("anon: I will fail everyone")
}

@Test
fun `when know user sends message, and changes its name, then show new name`() =
fun `when know user sends message, and all messages are less than one line long, and user changes their name, then show new name`() =
runTest {
every { users.latest() } returns ProfileResult.ok(mapOf("id" to Profile("id", "Tony KnowsItAll")))
every { messages.observe() } returns
flowOf(
MessageResult.ok(listOf(Message("id", "I will fail everyone"))),
)
val emissions = mutableListOf<String>()
val emissions = mutableListOf<List<String>>()
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)

every { users.latest() } returns ProfileResult.ok(mapOf("id" to Profile("id", "Neamah")))
every { messages.observe() } returns
flowOf(
MessageResult.ok(listOf(Message("id", "I hate my student, but not as much as i hate myself"))),
MessageResult.ok(listOf(Message("id", "I hate my students..."))),
)
backgroundScope.launch { displayableMessages().collect(emissions::add) }
advanceTimeBy(10.seconds)
emissions.shouldContainExactly(
emissions[0].shouldContainExactly(
"Tony KnowsItAll: I will fail everyone",
"Neamah: I hate my student, but not as much as i hate myself",
)
emissions[1].shouldContainExactly(
"Neamah: I hate my students...",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ViewMessagesPresenterTest {
@Test
fun `given message exist, when attached, then show message`() =
runTest(dispatcher) {
every { read() } returns flowOf("Kai: Biggleswad ez badd")
every { read() } returns flowOf(listOf("Kai: Biggleswad ez badd"))
presenter.attach(view)
advanceUntilIdle()
verifyOrder {
Expand Down Expand Up @@ -81,8 +81,8 @@ class ViewMessagesPresenterTest {
runTest(dispatcher) {
every { read() } returns
flowOf(
"Kai: Pokemon good",
"Allie: Barry is the best",
listOf("Kai: Pokemon good"),
listOf("Allie: Barry is the best"),
)

presenter.attach(view)
Expand All @@ -99,7 +99,7 @@ class ViewMessagesPresenterTest {
assertTrue { it is Messages }
assertEquals(
message,
(it as Messages).message,
(it as Messages).lines[0],
)
}
}
Expand Down
Loading