From aa7ea6fdbf5bf00a21a3bdd15079fa6b8b2b357c Mon Sep 17 00:00:00 2001 From: Ryo Takeuchi Date: Tue, 5 Dec 2023 21:11:20 +0900 Subject: [PATCH 1/5] =?UTF-8?q?:sparkles:=20=E3=82=B9=E3=82=B1=E3=82=B8?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=AB=E8=A9=B3=E7=B4=B0=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/nito/app/shared/NitoNavHost.kt | 2 + .../schedule/detail/ScheduleDetailEvent.kt | 5 ++ .../schedule/detail/ScheduleDetailIntent.kt | 9 ++ .../detail/ScheduleDetailNavigation.kt | 19 +++++ .../schedule/detail/ScheduleDetailScreen.kt | 85 +++++++++++++++++++ .../detail/ScheduleDetailScreenUiState.kt | 16 ++++ .../detail/ScheduleDetailStateMachine.kt | 75 ++++++++++++++++ .../detail/ScheduleDetailScreen.ios.kt | 16 ++++ 8 files changed, 227 insertions(+) create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailEvent.kt create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt create mode 100644 feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt create mode 100644 feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt diff --git a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt index a3674850..489f0c6a 100644 --- a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt +++ b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt @@ -7,6 +7,7 @@ import club.nito.core.model.AuthStatus import club.nito.feature.auth.loginNavigationRoute import club.nito.feature.auth.loginScreen import club.nito.feature.auth.navigateToLogin +import club.nito.feature.schedule.detail.scheduleDetailScreen import club.nito.feature.schedule.list.navigateToScheduleList import club.nito.feature.schedule.list.scheduleListScreen import club.nito.feature.settings.navigateToSettings @@ -57,6 +58,7 @@ fun NitoNavHost( }, ) scheduleListScreen() + scheduleDetailScreen() settingsScreen( onSignedOut = { navigator.navigateToLogin( diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailEvent.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailEvent.kt new file mode 100644 index 00000000..6a1b5ccf --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailEvent.kt @@ -0,0 +1,5 @@ +package club.nito.feature.schedule.detail + +public sealed class ScheduleDetailEvent { + public data class NavigateToScheduleDetail(val scheduleId: String) : ScheduleDetailEvent() +} diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt new file mode 100644 index 00000000..d70c9b82 --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt @@ -0,0 +1,9 @@ +package club.nito.feature.schedule.detail + +import club.nito.core.domain.model.ParticipantSchedule + +public sealed class ScheduleDetailIntent { + public data class ClickShowConfirmParticipateDialog(val schedule: ParticipantSchedule) : ScheduleDetailIntent() + public data class ClickParticipateSchedule(val schedule: ParticipantSchedule) : ScheduleDetailIntent() + public data object ClickDismissConfirmParticipateDialog : ScheduleDetailIntent() +} diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt new file mode 100644 index 00000000..9d402a75 --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt @@ -0,0 +1,19 @@ +package club.nito.feature.schedule.detail + +import moe.tlaster.precompose.navigation.NavOptions +import moe.tlaster.precompose.navigation.Navigator +import moe.tlaster.precompose.navigation.RouteBuilder + +public const val scheduleDetailNavigationRoute: String = "schedule_detail" + +public fun Navigator.navigateToScheduleDetail(navOptions: NavOptions? = null) { + this.navigate(scheduleDetailNavigationRoute, navOptions) +} + +public fun RouteBuilder.scheduleDetailScreen() { + scene( + route = scheduleDetailNavigationRoute, + ) { + ScheduleDetailRoute() + } +} diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt new file mode 100644 index 00000000..234e5a43 --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt @@ -0,0 +1,85 @@ +package club.nito.feature.schedule.detail + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import club.nito.core.designsystem.component.CenterAlignedTopAppBar +import club.nito.core.designsystem.component.Scaffold +import club.nito.core.designsystem.component.Text +import club.nito.core.ui.ConfirmParticipateDialog +import club.nito.core.ui.koinStateMachine +import club.nito.core.ui.message.SnackbarMessageEffect + +@Composable +public fun ScheduleDetailRoute( + stateMachine: ScheduleDetailStateMachine = koinStateMachine(ScheduleDetailStateMachine::class), +) { + stateMachine.event.collectAsState(initial = null).value?.let { + LaunchedEffect(it.hashCode()) { + when (it) { + is ScheduleDetailEvent.NavigateToScheduleDetail -> {} + } + stateMachine.consume(it) + } + } + + val uiState by stateMachine.uiState.collectAsState() + val snackbarHostState = remember { SnackbarHostState() } + + SnackbarMessageEffect( + snackbarHostState = snackbarHostState, + userMessageStateHolder = stateMachine.userMessageStateHolder, + ) + + ScheduleDetailScreen( + uiState = uiState, + snackbarHostState = snackbarHostState, + dispatch = stateMachine::dispatch, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun ScheduleDetailScreen( + uiState: ScheduleDetailScreenUiState, + snackbarHostState: SnackbarHostState, + dispatch: (ScheduleDetailIntent) -> Unit = {}, +) { + val confirmParticipateDialog = uiState.confirmParticipateDialog + + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { + Text( + text = "スケジュール一覧", + ) + }, + ) + }, + snackbarHost = { SnackbarHost(snackbarHostState) }, + content = { innerPadding -> + if (confirmParticipateDialog is ConfirmParticipateDialogUiState.Show) { + ConfirmParticipateDialog( + schedule = confirmParticipateDialog.schedule, + dateTimeFormatter = uiState.dateFormatter, + onParticipateRequest = { dispatch(ScheduleDetailIntent.ClickParticipateSchedule(it)) }, + onDismissRequest = { dispatch(ScheduleDetailIntent.ClickDismissConfirmParticipateDialog) }, + ) + } + + Column( + modifier = Modifier.padding(innerPadding), + ) { + } + }, + ) +} diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt new file mode 100644 index 00000000..ca581e58 --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt @@ -0,0 +1,16 @@ +package club.nito.feature.schedule.detail + +import club.nito.core.common.NitoDateFormatter +import club.nito.core.domain.model.ParticipantSchedule +import club.nito.core.model.FetchSingleContentResult + +public data class ScheduleDetailScreenUiState( + val dateFormatter: NitoDateFormatter, + val scheduleList: FetchSingleContentResult, + val confirmParticipateDialog: ConfirmParticipateDialogUiState, +) + +public sealed class ConfirmParticipateDialogUiState { + public data class Show(val schedule: ParticipantSchedule) : ConfirmParticipateDialogUiState() + public data object Hide : ConfirmParticipateDialogUiState() +} diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt new file mode 100644 index 00000000..9c974b36 --- /dev/null +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt @@ -0,0 +1,75 @@ +package club.nito.feature.schedule.detail + +import club.nito.core.common.NitoDateFormatter +import club.nito.core.domain.FetchParticipantScheduleByIdUseCase +import club.nito.core.domain.model.ParticipantSchedule +import club.nito.core.model.FetchSingleContentResult +import club.nito.core.ui.StateMachine +import club.nito.core.ui.buildUiState +import club.nito.core.ui.message.UserMessageStateHolder +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.viewModelScope + +public class ScheduleDetailStateMachine( + fetchParticipantScheduleById: FetchParticipantScheduleByIdUseCase, + public val userMessageStateHolder: UserMessageStateHolder, + private val dateTimeFormatter: NitoDateFormatter, +) : StateMachine(), + UserMessageStateHolder by userMessageStateHolder { + private val showConfirmParticipateSchedule = MutableStateFlow(null) + private val participantSchedule: MutableStateFlow> = + MutableStateFlow(FetchSingleContentResult.Loading) + + public val uiState: StateFlow = buildUiState( + showConfirmParticipateSchedule, + participantSchedule, + ) { showConfirmParticipateSchedule, participantSchedule -> + ScheduleDetailScreenUiState( + dateFormatter = dateTimeFormatter, + scheduleList = participantSchedule, + confirmParticipateDialog = showConfirmParticipateSchedule + ?.let(ConfirmParticipateDialogUiState::Show) + ?: ConfirmParticipateDialogUiState.Hide, + ) + } + + private val _events = MutableStateFlow>(emptyList()) + public val event: Flow = _events.map { it.firstOrNull() } + + init { + viewModelScope.launch { + participantSchedule.value = fetchParticipantScheduleById(id = "") + } + } + + public fun dispatch(intent: ScheduleDetailIntent) { + viewModelScope.launch { + when (intent) { + is ScheduleDetailIntent.ClickShowConfirmParticipateDialog -> { + showConfirmParticipateSchedule.emit(intent.schedule) + } + + is ScheduleDetailIntent.ClickParticipateSchedule -> { + showConfirmParticipateSchedule.emit(null) + + val scheduledAt = dateTimeFormatter.formatDateTime(intent.schedule.scheduledAt) + userMessageStateHolder.showMessage("$scheduledAt に参加登録しました 🎉") + } + + ScheduleDetailIntent.ClickDismissConfirmParticipateDialog -> { + showConfirmParticipateSchedule.emit(null) + } + } + } + } + + public fun consume(event: ScheduleDetailEvent) { + viewModelScope.launch { + _events.emit(_events.value.filterNot { it == event }) + } + } +} diff --git a/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt b/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt new file mode 100644 index 00000000..ffe03da1 --- /dev/null +++ b/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt @@ -0,0 +1,16 @@ +package club.nito.feature.schedule.detail + +import androidx.compose.ui.window.ComposeUIViewController +import club.nito.core.designsystem.theme.NitoTheme +import platform.UIKit.UIViewController + +@Suppress("FunctionName") +public fun ScheduleDetailRouteViewController( + stateMachine: ScheduleDetailStateMachine, +): UIViewController = ComposeUIViewController { + NitoTheme { + ScheduleDetailRoute( + stateMachine = stateMachine, + ) + } +} From e189404375191d94742e2c492d3b6b6172a133a5 Mon Sep 17 00:00:00 2001 From: Ryo Takeuchi Date: Tue, 5 Dec 2023 21:11:39 +0900 Subject: [PATCH 2/5] =?UTF-8?q?:sparkles:=20=E3=83=8A=E3=83=93=E3=82=B2?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/ScheduleDetailNavigation.kt | 19 +++++++++++++------ .../schedule/detail/ScheduleDetailScreen.kt | 7 ++++++- .../detail/ScheduleDetailStateMachine.kt | 4 +++- .../detail/ScheduleDetailScreen.ios.kt | 3 +++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt index 9d402a75..d6531030 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailNavigation.kt @@ -1,19 +1,26 @@ package club.nito.feature.schedule.detail +import club.nito.core.model.schedule.ScheduleId import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.Navigator import moe.tlaster.precompose.navigation.RouteBuilder +import moe.tlaster.precompose.navigation.path -public const val scheduleDetailNavigationRoute: String = "schedule_detail" +private const val ROUTE: String = "/schedules" -public fun Navigator.navigateToScheduleDetail(navOptions: NavOptions? = null) { - this.navigate(scheduleDetailNavigationRoute, navOptions) +public fun Navigator.navigateToScheduleDetail( + id: ScheduleId, + navOptions: NavOptions? = null, +) { + this.navigate("$ROUTE/$id", navOptions) } public fun RouteBuilder.scheduleDetailScreen() { scene( - route = scheduleDetailNavigationRoute, - ) { - ScheduleDetailRoute() + route = "$ROUTE/{id}", + ) { backStackEntry -> + backStackEntry.path("id")?.let { id -> + ScheduleDetailRoute(id = id) + } } } diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt index 234e5a43..417a4176 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt @@ -14,13 +14,18 @@ import androidx.compose.ui.Modifier import club.nito.core.designsystem.component.CenterAlignedTopAppBar import club.nito.core.designsystem.component.Scaffold import club.nito.core.designsystem.component.Text +import club.nito.core.model.schedule.ScheduleId import club.nito.core.ui.ConfirmParticipateDialog import club.nito.core.ui.koinStateMachine import club.nito.core.ui.message.SnackbarMessageEffect +import org.koin.core.parameter.parametersOf @Composable public fun ScheduleDetailRoute( - stateMachine: ScheduleDetailStateMachine = koinStateMachine(ScheduleDetailStateMachine::class), + id: ScheduleId, + stateMachine: ScheduleDetailStateMachine = koinStateMachine(ScheduleDetailStateMachine::class) { + parametersOf(id) + }, ) { stateMachine.event.collectAsState(initial = null).value?.let { LaunchedEffect(it.hashCode()) { diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt index 9c974b36..8f310634 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt @@ -4,6 +4,7 @@ import club.nito.core.common.NitoDateFormatter import club.nito.core.domain.FetchParticipantScheduleByIdUseCase import club.nito.core.domain.model.ParticipantSchedule import club.nito.core.model.FetchSingleContentResult +import club.nito.core.model.schedule.ScheduleId import club.nito.core.ui.StateMachine import club.nito.core.ui.buildUiState import club.nito.core.ui.message.UserMessageStateHolder @@ -15,6 +16,7 @@ import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.viewModelScope public class ScheduleDetailStateMachine( + id: ScheduleId, fetchParticipantScheduleById: FetchParticipantScheduleByIdUseCase, public val userMessageStateHolder: UserMessageStateHolder, private val dateTimeFormatter: NitoDateFormatter, @@ -42,7 +44,7 @@ public class ScheduleDetailStateMachine( init { viewModelScope.launch { - participantSchedule.value = fetchParticipantScheduleById(id = "") + participantSchedule.value = fetchParticipantScheduleById(id = id) } } diff --git a/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt b/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt index ffe03da1..4b16c81c 100644 --- a/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt +++ b/feature/schedule/src/iosMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.ios.kt @@ -2,14 +2,17 @@ package club.nito.feature.schedule.detail import androidx.compose.ui.window.ComposeUIViewController import club.nito.core.designsystem.theme.NitoTheme +import club.nito.core.model.schedule.ScheduleId import platform.UIKit.UIViewController @Suppress("FunctionName") public fun ScheduleDetailRouteViewController( + id: ScheduleId, stateMachine: ScheduleDetailStateMachine, ): UIViewController = ComposeUIViewController { NitoTheme { ScheduleDetailRoute( + id = id, stateMachine = stateMachine, ) } From 4956984e49595bd424e0ab6fe267984ee332493a Mon Sep 17 00:00:00 2001 From: Ryo Takeuchi Date: Tue, 5 Dec 2023 21:11:52 +0900 Subject: [PATCH 3/5] =?UTF-8?q?:sparkles:=20=E3=82=B9=E3=82=B1=E3=82=B8?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=AB=E8=A9=B3=E7=B4=B0=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=B8=E3=81=AE=E7=94=BB=E9=9D=A2=E9=81=B7=E7=A7=BB=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/club/nito/app/shared/NitoNavHost.kt | 2 ++ .../nito/feature/schedule/di/ScheduleFeatureModule.kt | 10 ++++++++++ .../kotlin/club/nito/feature/top/TopNavigation.kt | 3 +++ .../kotlin/club/nito/feature/top/TopScreen.kt | 3 +++ .../kotlin/club/nito/feature/top/TopScreenEvent.kt | 7 +++++++ .../club/nito/feature/top/TopScreenStateMachine.kt | 6 +++++- 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt index 489f0c6a..3033cc6d 100644 --- a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt +++ b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoNavHost.kt @@ -7,6 +7,7 @@ import club.nito.core.model.AuthStatus import club.nito.feature.auth.loginNavigationRoute import club.nito.feature.auth.loginScreen import club.nito.feature.auth.navigateToLogin +import club.nito.feature.schedule.detail.navigateToScheduleDetail import club.nito.feature.schedule.detail.scheduleDetailScreen import club.nito.feature.schedule.list.navigateToScheduleList import club.nito.feature.schedule.list.scheduleListScreen @@ -42,6 +43,7 @@ fun NitoNavHost( ) topScreen( + onRecentScheduleClicked = navigator::navigateToScheduleDetail, onScheduleListClick = navigator::navigateToScheduleList, onSettingsClick = navigator::navigateToSettings, ) diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/di/ScheduleFeatureModule.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/di/ScheduleFeatureModule.kt index 09d11262..1c480d68 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/di/ScheduleFeatureModule.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/di/ScheduleFeatureModule.kt @@ -1,5 +1,7 @@ package club.nito.feature.schedule.di +import club.nito.core.model.schedule.ScheduleId +import club.nito.feature.schedule.detail.ScheduleDetailStateMachine import club.nito.feature.schedule.list.ScheduleListStateMachine import org.koin.core.module.Module import org.koin.dsl.module @@ -12,4 +14,12 @@ public val scheduleFeatureModule: Module = module { dateFormatter = get(), ) } + factory { (id: ScheduleId) -> + ScheduleDetailStateMachine( + id = id, + fetchParticipantScheduleById = get(), + userMessageStateHolder = get(), + dateTimeFormatter = get(), + ) + } } diff --git a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopNavigation.kt b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopNavigation.kt index b18210d8..0e947e58 100644 --- a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopNavigation.kt +++ b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopNavigation.kt @@ -1,5 +1,6 @@ package club.nito.feature.top +import club.nito.core.model.schedule.ScheduleId import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.Navigator import moe.tlaster.precompose.navigation.RouteBuilder @@ -11,6 +12,7 @@ public fun Navigator.navigateToTop(navOptions: NavOptions? = null) { } public fun RouteBuilder.topScreen( + onRecentScheduleClicked: (ScheduleId) -> Unit = {}, onScheduleListClick: () -> Unit = {}, onSettingsClick: () -> Unit = {}, ) { @@ -18,6 +20,7 @@ public fun RouteBuilder.topScreen( route = topNavigationRoute, ) { TopRoute( + onRecentScheduleClicked = onRecentScheduleClicked, onScheduleListClick = onScheduleListClick, onSettingsClick = onSettingsClick, ) diff --git a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreen.kt b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreen.kt index 3094c308..8a1dcade 100644 --- a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreen.kt +++ b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import club.nito.core.model.schedule.ScheduleId import club.nito.core.ui.ConfirmParticipateDialog import club.nito.core.ui.koinStateMachine import club.nito.core.ui.message.SnackbarMessageEffect @@ -28,12 +29,14 @@ import club.nito.feature.top.component.ParticipantScheduleSection @Composable public fun TopRoute( stateMachine: TopScreenStateMachine = koinStateMachine(TopScreenStateMachine::class), + onRecentScheduleClicked: (ScheduleId) -> Unit = {}, onScheduleListClick: () -> Unit = {}, onSettingsClick: () -> Unit = {}, ) { stateMachine.event.collectAsState(initial = null).value?.let { LaunchedEffect(it.hashCode()) { when (it) { + is TopScreenEvent.OnRecentScheduleClicked -> onRecentScheduleClicked(it.scheduleId) TopScreenEvent.NavigateToScheduleList -> onScheduleListClick() TopScreenEvent.NavigateToSettings -> onSettingsClick() } diff --git a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenEvent.kt b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenEvent.kt index d7323dd2..526de9f6 100644 --- a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenEvent.kt +++ b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenEvent.kt @@ -1,6 +1,13 @@ package club.nito.feature.top +import club.nito.core.model.schedule.ScheduleId + public sealed class TopScreenEvent { + /** + * Navigate to schedule list screen + */ + public data class OnRecentScheduleClicked(val scheduleId: ScheduleId) : TopScreenEvent() + /** * Navigate to schedule list screen */ diff --git a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenStateMachine.kt b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenStateMachine.kt index 070b7b3a..1798f92a 100644 --- a/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenStateMachine.kt +++ b/feature/top/src/commonMain/kotlin/club/nito/feature/top/TopScreenStateMachine.kt @@ -48,7 +48,11 @@ public class TopScreenStateMachine( public fun dispatch(intent: TopScreenIntent) { stateMachineScope.launch { when (intent) { - is TopScreenIntent.ClickShowConfirmParticipateDialog -> showConfirmParticipateSchedule.emit(intent.schedule) + is TopScreenIntent.ClickShowConfirmParticipateDialog -> { + _events.emit(_events.value + TopScreenEvent.OnRecentScheduleClicked(intent.schedule.id)) +// showConfirmParticipateSchedule.emit(intent.schedule) + } + is TopScreenIntent.ClickParticipateSchedule -> { showConfirmParticipateSchedule.emit(null) From bc203aff339ff121e32764631426f5aef1d5d498 Mon Sep 17 00:00:00 2001 From: Ryo Takeuchi Date: Tue, 5 Dec 2023 21:12:14 +0900 Subject: [PATCH 4/5] =?UTF-8?q?:sparkles:=20=E3=82=B9=E3=82=B1=E3=82=B8?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=AB=E8=A9=B3=E7=B4=B0=E7=94=BB=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/detail/ScheduleDetailIntent.kt | 2 +- .../schedule/detail/ScheduleDetailScreen.kt | 371 +++++++++++++++++- .../detail/ScheduleDetailScreenUiState.kt | 2 +- .../detail/ScheduleDetailStateMachine.kt | 4 +- 4 files changed, 362 insertions(+), 17 deletions(-) diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt index d70c9b82..6859daa4 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailIntent.kt @@ -3,7 +3,7 @@ package club.nito.feature.schedule.detail import club.nito.core.domain.model.ParticipantSchedule public sealed class ScheduleDetailIntent { - public data class ClickShowConfirmParticipateDialog(val schedule: ParticipantSchedule) : ScheduleDetailIntent() + public data class ClickParticipate(val schedule: ParticipantSchedule) : ScheduleDetailIntent() public data class ClickParticipateSchedule(val schedule: ParticipantSchedule) : ScheduleDetailIntent() public data object ClickDismissConfirmParticipateDialog : ScheduleDetailIntent() } diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt index 417a4176..c3fde71f 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreen.kt @@ -1,23 +1,72 @@ package club.nito.feature.schedule.detail +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Send +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import club.nito.core.common.NitoDateFormatter import club.nito.core.designsystem.component.CenterAlignedTopAppBar import club.nito.core.designsystem.component.Scaffold import club.nito.core.designsystem.component.Text +import club.nito.core.domain.model.ParticipantSchedule +import club.nito.core.model.FetchSingleContentResult import club.nito.core.model.schedule.ScheduleId -import club.nito.core.ui.ConfirmParticipateDialog +import club.nito.core.ui.ProfileImage import club.nito.core.ui.koinStateMachine import club.nito.core.ui.message.SnackbarMessageEffect +import com.seiko.imageloader.rememberImagePainter import org.koin.core.parameter.parametersOf @Composable @@ -58,33 +107,329 @@ private fun ScheduleDetailScreen( snackbarHostState: SnackbarHostState, dispatch: (ScheduleDetailIntent) -> Unit = {}, ) { - val confirmParticipateDialog = uiState.confirmParticipateDialog + val layoutDirection = LocalLayoutDirection.current + val localDensity = LocalDensity.current + + val schedule = uiState.schedule + var sendMessageContainerHeightDp by remember { + mutableStateOf(0.dp) + } Scaffold( topBar = { CenterAlignedTopAppBar( title = { Text( - text = "スケジュール一覧", + text = "スケジュール詳細", ) }, + navigationIcon = { + IconButton(onClick = { }) { + Icon(Icons.Default.ArrowBack, contentDescription = "Back") + } + }, ) }, snackbarHost = { SnackbarHost(snackbarHostState) }, content = { innerPadding -> - if (confirmParticipateDialog is ConfirmParticipateDialogUiState.Show) { - ConfirmParticipateDialog( - schedule = confirmParticipateDialog.schedule, - dateTimeFormatter = uiState.dateFormatter, - onParticipateRequest = { dispatch(ScheduleDetailIntent.ClickParticipateSchedule(it)) }, - onDismissRequest = { dispatch(ScheduleDetailIntent.ClickDismissConfirmParticipateDialog) }, + Box( + modifier = Modifier.fillMaxSize(), + ) { + when (schedule) { + FetchSingleContentResult.Loading -> CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + ) + + FetchSingleContentResult.NoContent -> Text( + text = "スケジュールが見つかりませんでした", + modifier = Modifier.align(Alignment.Center), + ) + + is FetchSingleContentResult.Success -> { + val containerModifier = Modifier + .padding( + start = innerPadding.calculateStartPadding(layoutDirection), + end = innerPadding.calculateEndPadding(layoutDirection), + ) + .fillMaxWidth() + .padding(horizontal = 16.dp) + + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .padding( + top = innerPadding.calculateTopPadding(), + bottom = innerPadding.calculateBottomPadding(), + ) + .padding(bottom = sendMessageContainerHeightDp) + .padding(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(40.dp), + ) { + VenueSection( + schedule = schedule.data, + dateFormatter = uiState.dateFormatter, + modifier = containerModifier, + ) + + MeetSection( + schedule = schedule.data, + dateFormatter = uiState.dateFormatter, + modifier = containerModifier, + ) + + ParticipantSection( + schedule = schedule.data, + onParticipateClick = { dispatch(ScheduleDetailIntent.ClickParticipate(it)) }, + modifier = containerModifier, + ) + } + } + + is FetchSingleContentResult.Failure -> Text( + text = schedule.error?.message ?: "エラーが発生しました", + modifier = Modifier.align(Alignment.Center), + ) + } + + SendMessageContainer( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .onGloballyPositioned { coordinates -> + sendMessageContainerHeightDp = with(localDensity) { coordinates.size.height.toDp() } + }, + innerPadding = innerPadding, ) } + }, + ) +} + +@Composable +private fun VenueSection( + schedule: ParticipantSchedule, + dateFormatter: NitoDateFormatter, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip(RoundedCornerShape(16.dp)) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.onSecondary, + shape = RoundedCornerShape(16.dp), + ), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(max = 200.dp), + ) { + Image( + painter = rememberImagePainter( + url = schedule.venue.imageUrl, + ), + modifier = Modifier.fillMaxWidth(), + contentScale = ContentScale.FillWidth, + contentDescription = null, + ) + + Text( + text = "開催情報", + modifier = Modifier + .align(Alignment.TopStart) + .padding(16.dp) + .background( + color = Color.White.copy(alpha = 0.5f), + shape = CircleShape, + ) + .padding(vertical = 4.dp, horizontal = 8.dp), + fontSize = 14.sp, + color = Color.Black, + ) + } + + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Text( + text = schedule.venue.name, + ) + Text( + text = dateFormatter.formatDateTime(schedule.scheduledAt), + ) + Text( + text = schedule.description, + ) + } + } +} + +@Composable +private fun MeetSection( + schedule: ParticipantSchedule, + dateFormatter: NitoDateFormatter, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip(RoundedCornerShape(16.dp)) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.onSecondary, + shape = RoundedCornerShape(16.dp), + ), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(max = 200.dp), + ) { + Image( + painter = rememberImagePainter( + url = schedule.meet.imageUrl, + ), + modifier = Modifier.fillMaxWidth(), + contentScale = ContentScale.FillWidth, + contentDescription = null, + ) - Column( - modifier = Modifier.padding(innerPadding), + Text( + text = "集合情報", + modifier = Modifier + .align(Alignment.TopStart) + .padding(16.dp) + .background( + color = Color.White.copy(alpha = 0.5f), + shape = CircleShape, + ) + .padding(vertical = 4.dp, horizontal = 8.dp), + fontSize = 14.sp, + color = Color.Black, + ) + } + + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Text( + text = schedule.meet.name, + ) + Text( + text = dateFormatter.formatDateTime(schedule.metAt), + ) + } + } +} + +@Composable +private fun ParticipantSection( + schedule: ParticipantSchedule, + onParticipateClick: (ParticipantSchedule) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Text( + text = "参加情報", + ) + LazyRow( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), + verticalAlignment = Alignment.CenterVertically, + ) { + items( + items = schedule.participants, + key = { profile -> profile.id }, + ) { profile -> + ProfileImage( + profile = profile, + modifier = Modifier.size(48.dp), + ) + } + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + OutlinedButton( + onClick = { onParticipateClick(schedule) }, ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null, + modifier = Modifier.size(16.dp), + ) + Spacer(modifier = Modifier.size(8.dp)) + Text(text = "参加する") } - }, - ) + } + } +} + +@Composable +private fun SendMessageContainer( + modifier: Modifier = Modifier, + backgroundColor: Color = MaterialTheme.colorScheme.secondaryContainer, + innerPadding: PaddingValues = PaddingValues(), + layoutDirection: LayoutDirection = LocalLayoutDirection.current, +) { + val insetPadding = remember(modifier, innerPadding, layoutDirection) { + PaddingValues( + start = innerPadding.calculateStartPadding(layoutDirection = layoutDirection), + end = innerPadding.calculateEndPadding(layoutDirection = layoutDirection), + bottom = innerPadding.calculateBottomPadding(), + ) + } + + Row( + modifier = modifier + .background( + color = backgroundColor, + shape = RoundedCornerShape( + topStart = 16.dp, + topEnd = 16.dp, + ), + ) + .padding(insetPadding) + .heightIn(min = 80.dp) + .padding(horizontal = 16.dp) + .padding( + top = 16.dp, + bottom = 8.dp, + ), + horizontalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Start), + ) { + OutlinedTextField( + value = "", + onValueChange = { }, + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1f), + enabled = false, + placeholder = { Text(text = "Coming Soon.") }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Done, + ), + ) + + IconButton( + onClick = { }, + Modifier.align(Alignment.CenterVertically), + enabled = false, + ) { + Icon(Icons.Default.Send, contentDescription = "Send") + } + } } diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt index ca581e58..5576c6aa 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailScreenUiState.kt @@ -6,7 +6,7 @@ import club.nito.core.model.FetchSingleContentResult public data class ScheduleDetailScreenUiState( val dateFormatter: NitoDateFormatter, - val scheduleList: FetchSingleContentResult, + val schedule: FetchSingleContentResult, val confirmParticipateDialog: ConfirmParticipateDialogUiState, ) diff --git a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt index 8f310634..baf30b7d 100644 --- a/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt +++ b/feature/schedule/src/commonMain/kotlin/club/nito/feature/schedule/detail/ScheduleDetailStateMachine.kt @@ -32,7 +32,7 @@ public class ScheduleDetailStateMachine( ) { showConfirmParticipateSchedule, participantSchedule -> ScheduleDetailScreenUiState( dateFormatter = dateTimeFormatter, - scheduleList = participantSchedule, + schedule = participantSchedule, confirmParticipateDialog = showConfirmParticipateSchedule ?.let(ConfirmParticipateDialogUiState::Show) ?: ConfirmParticipateDialogUiState.Hide, @@ -51,7 +51,7 @@ public class ScheduleDetailStateMachine( public fun dispatch(intent: ScheduleDetailIntent) { viewModelScope.launch { when (intent) { - is ScheduleDetailIntent.ClickShowConfirmParticipateDialog -> { + is ScheduleDetailIntent.ClickParticipate -> { showConfirmParticipateSchedule.emit(intent.schedule) } From 6bbac97ff8748419af4566afa82ff1d3f1a7d9b4 Mon Sep 17 00:00:00 2001 From: Ryo Takeuchi Date: Tue, 5 Dec 2023 21:12:39 +0900 Subject: [PATCH 5/5] =?UTF-8?q?:sparkles:=20iOS=20=E5=81=B4=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Navigation/RootStateMachine.swift | 1 + .../Modules/Sources/Navigation/RootView.swift | 5 ++++ .../ComposeScheduleDetailScreen.swift | 28 +++++++++++++++++++ .../Modules/Sources/Schedule/Schedule.swift | 1 - .../Sources/Top/ComposeTopScreen.swift | 4 +++ .../club/nito/feature/top/TopScreen.ios.kt | 3 ++ 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 app/ios/Modules/Sources/Schedule/ComposeScheduleDetailScreen.swift delete mode 100644 app/ios/Modules/Sources/Schedule/Schedule.swift diff --git a/app/ios/Modules/Sources/Navigation/RootStateMachine.swift b/app/ios/Modules/Sources/Navigation/RootStateMachine.swift index 34d494e8..052ca9fb 100644 --- a/app/ios/Modules/Sources/Navigation/RootStateMachine.swift +++ b/app/ios/Modules/Sources/Navigation/RootStateMachine.swift @@ -17,6 +17,7 @@ enum Routing: Hashable { case login case top case scheduleList + case scheduleDetail(scheduleId: String) case settings } diff --git a/app/ios/Modules/Sources/Navigation/RootView.swift b/app/ios/Modules/Sources/Navigation/RootView.swift index 3aa25870..1d66dcac 100644 --- a/app/ios/Modules/Sources/Navigation/RootView.swift +++ b/app/ios/Modules/Sources/Navigation/RootView.swift @@ -26,6 +26,9 @@ public struct RootView: View { .ignoresSafeArea(.keyboard) case .top: ComposeTopScreen( + onRecentScheduleClicked: { scheduleId in + stateMachine.dispatch(intent: .routing(.scheduleDetail(scheduleId: scheduleId))) + }, onScheduleListButtonClick: { stateMachine.dispatch(intent: .routing(.scheduleList)) }, @@ -36,6 +39,8 @@ public struct RootView: View { .navigationBarBackButtonHidden(true) case .scheduleList: ComposeScheduleListScreen() + case .scheduleDetail(let scheduleId): + ComposeScheduleDetailScreen(scheduleId: scheduleId) case .settings: ComposeSettingsScreen() } diff --git a/app/ios/Modules/Sources/Schedule/ComposeScheduleDetailScreen.swift b/app/ios/Modules/Sources/Schedule/ComposeScheduleDetailScreen.swift new file mode 100644 index 00000000..fffe0ef3 --- /dev/null +++ b/app/ios/Modules/Sources/Schedule/ComposeScheduleDetailScreen.swift @@ -0,0 +1,28 @@ +import KmpContainer +import NitoKmp +import SwiftUI +import UIKit + +public struct ComposeScheduleDetailScreen: UIViewControllerRepresentable { + private let scheduleId: String + + public init(scheduleId: String) { + self.scheduleId = scheduleId + } + + public func makeUIViewController(context: Context) -> UIViewController { + return ScheduleDetailScreen_iosKt.ScheduleDetailRouteViewController( + id: scheduleId, + stateMachine: ScheduleDetailStateMachine( + id: scheduleId, + fetchParticipantScheduleById: Container.shared.get( + type: FetchParticipantScheduleByIdUseCase.self), + userMessageStateHolder: Container.shared.get(type: UserMessageStateHolder.self), + dateTimeFormatter: Container.shared.get(type: CommonNitoDateFormatter.self) + ) + ) + } + + public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + } +} diff --git a/app/ios/Modules/Sources/Schedule/Schedule.swift b/app/ios/Modules/Sources/Schedule/Schedule.swift deleted file mode 100644 index fecc4ab4..00000000 --- a/app/ios/Modules/Sources/Schedule/Schedule.swift +++ /dev/null @@ -1 +0,0 @@ -import Foundation diff --git a/app/ios/Modules/Sources/Top/ComposeTopScreen.swift b/app/ios/Modules/Sources/Top/ComposeTopScreen.swift index 88a2cb6d..3b383aa7 100644 --- a/app/ios/Modules/Sources/Top/ComposeTopScreen.swift +++ b/app/ios/Modules/Sources/Top/ComposeTopScreen.swift @@ -4,13 +4,16 @@ import SwiftUI import UIKit public struct ComposeTopScreen: UIViewControllerRepresentable { + private let onRecentScheduleClicked: (String) -> Void private let onScheduleListButtonClick: () -> Void private let onSettingsButtonClick: () -> Void public init( + onRecentScheduleClicked: @escaping (String) -> Void, onScheduleListButtonClick: @escaping () -> Void, onSettingsButtonClick: @escaping () -> Void ) { + self.onRecentScheduleClicked = onRecentScheduleClicked self.onScheduleListButtonClick = onScheduleListButtonClick self.onSettingsButtonClick = onSettingsButtonClick } @@ -22,6 +25,7 @@ public struct ComposeTopScreen: UIViewControllerRepresentable { userMessageStateHolder: Container.shared.get(type: UserMessageStateHolder.self), dateTimeFormatter: Container.shared.get(type: CommonNitoDateFormatter.self) ), + onRecentScheduleClicked: onRecentScheduleClicked, onScheduleListClick: onScheduleListButtonClick, onSettingsClick: onSettingsButtonClick ) diff --git a/feature/top/src/iosMain/kotlin/club/nito/feature/top/TopScreen.ios.kt b/feature/top/src/iosMain/kotlin/club/nito/feature/top/TopScreen.ios.kt index bafb8251..d114118f 100644 --- a/feature/top/src/iosMain/kotlin/club/nito/feature/top/TopScreen.ios.kt +++ b/feature/top/src/iosMain/kotlin/club/nito/feature/top/TopScreen.ios.kt @@ -2,17 +2,20 @@ package club.nito.feature.top import androidx.compose.ui.window.ComposeUIViewController import club.nito.core.designsystem.theme.NitoTheme +import club.nito.core.model.schedule.ScheduleId import platform.UIKit.UIViewController @Suppress("FunctionName") public fun TopRouteViewController( stateMachine: TopScreenStateMachine, + onRecentScheduleClicked: (ScheduleId) -> Unit = {}, onScheduleListClick: () -> Unit = {}, onSettingsClick: () -> Unit = {}, ): UIViewController = ComposeUIViewController { NitoTheme { TopRoute( stateMachine = stateMachine, + onRecentScheduleClicked = onRecentScheduleClicked, onScheduleListClick = onScheduleListClick, onSettingsClick = onSettingsClick, )