Skip to content

Commit

Permalink
Merge pull request #1026 from Corvus400/feature/implement_message_sec…
Browse files Browse the repository at this point in the history
…tion_timetable_item_detail_screen

✨ [TimetableItemDetailScreen] I added a UI to display a message in the event of cancellation.
  • Loading branch information
takahirom authored Sep 10, 2024
2 parents 2daf030 + 30a945f commit 92f49c7
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.github.droidkaigi.confsched.data.sessions.response.CategoryResponse
import io.github.droidkaigi.confsched.data.sessions.response.LocaledResponse
import io.github.droidkaigi.confsched.data.sessions.response.RoomResponse
import io.github.droidkaigi.confsched.data.sessions.response.SessionAssetResponse
import io.github.droidkaigi.confsched.data.sessions.response.SessionMessageResponse
import io.github.droidkaigi.confsched.data.sessions.response.SessionResponse
import io.github.droidkaigi.confsched.data.sessions.response.SessionsAllResponse
import io.github.droidkaigi.confsched.data.sessions.response.SpeakerResponse
Expand Down Expand Up @@ -48,6 +49,12 @@ public class FakeSessionsApiClient : SessionsApiClient {
}
}

public data object OperationalMessageExists : Status() {
override suspend fun sessionsAllResponse(): SessionsAllResponse {
return SessionsAllResponse.messageExistsFake()
}
}

public data object Error : Status() {
override suspend fun sessionsAllResponse(): SessionsAllResponse {
throw IOException("Fake IO Exception")
Expand Down Expand Up @@ -126,6 +133,17 @@ public fun SessionsAllResponse.Companion.onlyVideoAssetAvailableFake(): Sessions
),
)

public fun SessionsAllResponse.Companion.messageExistsFake(): SessionsAllResponse = SessionsAllResponse.fake(
sessions = SessionResponse.fakes(
message = SessionMessageResponse.fake(),
),
)

public fun SessionMessageResponse.Companion.fake(): SessionMessageResponse = SessionMessageResponse(
ja = "このセッションは中止になりました",
en = "This session has been canceled.",
)

private fun RoomResponse.Companion.fakes(): List<RoomResponse> = listOf(
RoomResponse(name = LocaledResponse(ja = "Hedgehog ja", en = "Hedgehog"), id = 1, sort = 1),
RoomResponse(
Expand Down Expand Up @@ -180,6 +198,7 @@ private fun SessionResponse.Companion.fakes(
rooms: List<RoomResponse> = RoomResponse.fakes(),
categories: List<CategoryResponse> = CategoryResponse.fakes(),
asset: SessionAssetResponse = SessionAssetResponse.fake(),
message: SessionMessageResponse? = null,
): List<SessionResponse> {
val sessions = mutableListOf<SessionResponse>()

Expand All @@ -204,7 +223,7 @@ private fun SessionResponse.Companion.fakes(
sessionCategoryItemId = 3,
interpretationTarget = false,
asset = asset,
message = null,
message = message,
sessionType = "WELCOME_TALK",
levels = listOf("UNSPECIFIED"),
),
Expand Down Expand Up @@ -266,7 +285,7 @@ private fun SessionResponse.Companion.fakes(
roomId = room.id,
sessionCategoryItemId = sessionCategoryItemId,
sessionType = "NORMAL",
message = null,
message = message,
isPlenumSession = false,
targetAudience = "For App developer アプリ開発者向け",
interpretationTarget = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import io.github.droidkaigi.confsched.testing.robot.TimetableItemCardRobot.Langu
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.Error
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.Operational
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.OperationalBothAssetAvailable
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.OperationalMessageExists
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.OperationalOnlySlideAssetAvailable
import io.github.droidkaigi.confsched.testing.robot.TimetableServerRobot.ServerStatus.OperationalOnlyVideoAssetAvailable
import io.github.droidkaigi.confsched.testing.rules.RobotTestRule
Expand Down Expand Up @@ -333,6 +334,7 @@ interface TimetableServerRobot {
OperationalBothAssetAvailable,
OperationalOnlySlideAssetAvailable,
OperationalOnlyVideoAssetAvailable,
OperationalMessageExists,
Error,
}

Expand All @@ -349,6 +351,7 @@ class DefaultTimetableServerRobot @Inject constructor(sessionsApiClient: Session
OperationalBothAssetAvailable -> Status.OperationalBothAssetAvailable
OperationalOnlySlideAssetAvailable -> Status.OperationalOnlySlideAssetAvailable
OperationalOnlyVideoAssetAvailable -> Status.OperationalOnlyVideoAssetAvailable
OperationalMessageExists -> Status.OperationalMessageExists
Error -> Status.Error
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import com.github.takahirom.roborazzi.captureRoboImage
import io.github.droidkaigi.confsched.data.sessions.FakeSessionsApiClient
import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailBookmarkIconTestTag
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailMessageRowTestTag
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailMessageRowTextTestTag
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailScreen
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailScreenLazyColumnTestTag
import io.github.droidkaigi.confsched.sessions.component.DescriptionMoreButtonTestTag
Expand Down Expand Up @@ -118,6 +120,18 @@ class TimetableItemDetailScreenRobot @Inject constructor(
.performScrollToNode(hasTestTag(TimetableItemDetailContentArchiveSectionTestTag))
}

fun scrollToMessageRow() {
composeTestRule
.onNode(hasTestTag(TimetableItemDetailScreenLazyColumnTestTag))
.performScrollToNode(hasTestTag(TimetableItemDetailMessageRowTestTag))

// FIXME Without this, you won't be able to scroll to the exact middle of the message section.
composeTestRule.onRoot().performTouchInput {
swipeUp(startY = centerY, endY = centerY - 175)
}
waitUntilIdle()
}

fun checkScreenCapture() {
composeTestRule
.onNode(isRoot())
Expand Down Expand Up @@ -248,6 +262,15 @@ class TimetableItemDetailScreenRobot @Inject constructor(
.assertDoesNotExist()
}

fun checkMessageDisplayed() {
composeTestRule
.onAllNodes(hasTestTag(TimetableItemDetailMessageRowTextTestTag))
.onFirst()
.assertExists()
.assertIsDisplayed()
.assertTextEquals("This session has been canceled.")
}

companion object {
val defaultSessionId = FakeSessionsApiClient.defaultSession.id
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,22 @@ class TimetableItemDetailScreenTest(private val testCase: DescribedBehavior<Time
}
}
}
describe("when server is operational exists message") {
doIt {
setupTimetableServer(ServerStatus.OperationalMessageExists)
}
describe("when launch") {
doIt {
setupScreenContent()
scrollToMessageRow()
}
itShould("display message") {
captureScreenWithChecks {
checkMessageDisplayed()
}
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
Expand All @@ -25,9 +34,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import conference_app_2024.feature.sessions.generated.resources.image
import io.github.droidkaigi.confsched.compose.EventFlow
import io.github.droidkaigi.confsched.compose.rememberEventFlow
import io.github.droidkaigi.confsched.designsystem.component.LoadingText
Expand All @@ -42,6 +54,7 @@ import io.github.droidkaigi.confsched.droidkaigiui.compositionlocal.LocalSnackba
import io.github.droidkaigi.confsched.model.Lang
import io.github.droidkaigi.confsched.model.TimetableItem
import io.github.droidkaigi.confsched.model.TimetableItem.Session
import io.github.droidkaigi.confsched.model.TimetableItem.Special
import io.github.droidkaigi.confsched.model.TimetableItemId
import io.github.droidkaigi.confsched.model.fake
import io.github.droidkaigi.confsched.sessions.TimetableItemDetailScreenUiState.Loaded
Expand All @@ -52,11 +65,14 @@ import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailHead
import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailSummaryCard
import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailTopAppBar
import io.github.droidkaigi.confsched.sessions.navigation.TimetableItemDetailDestination
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview

const val timetableItemDetailScreenRouteItemIdParameterName = "timetableItemId"
const val TimetableItemDetailBookmarkIconTestTag = "TimetableItemDetailBookmarkIconTestTag"
const val TimetableItemDetailScreenLazyColumnTestTag = "TimetableItemDetailScreenLazyColumnTestTag"
const val TimetableItemDetailMessageRowTestTag = "TimetableItemDetailMessageRowTestTag"
const val TimetableItemDetailMessageRowTextTestTag = "TimetableItemDetailMessageRowTextTestTag"

fun NavGraphBuilder.sessionScreens(
onNavigationIconClick: () -> Unit,
Expand Down Expand Up @@ -233,6 +249,41 @@ private fun TimetableItemDetailScreen(
)
}

when (uiState.timetableItem) {
is Session -> uiState.timetableItem.message
is Special -> uiState.timetableItem.message
}?.let {
item {
Row(
modifier = Modifier
.padding(
start = 8.dp,
top = 24.dp,
end = 8.dp,
bottom = 4.dp,
)
.height(IntrinsicSize.Min)
.testTag(TimetableItemDetailMessageRowTestTag),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Icon(
modifier = Modifier.fillMaxHeight(),
imageVector = Icons.Filled.Info,
contentDescription = stringResource(SessionsRes.string.image),
tint = MaterialTheme.colorScheme.error,
)
Text(
modifier = Modifier.testTag(
TimetableItemDetailMessageRowTextTestTag,
),
text = it.currentLangTitle,
fontSize = 16.sp,
color = MaterialTheme.colorScheme.error,
)
}
}
}

item {
TimetableItemDetailSummaryCard(
timetableItem = uiState.timetableItem,
Expand Down

0 comments on commit 92f49c7

Please sign in to comment.