From 310f6dede781d7e4ae4e1b77a6232bb4985e2e57 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 8 Apr 2026 19:39:22 +0000
Subject: [PATCH 1/3] Display speaker talks in speaker detail screen
Agent-Logs-Url: https://github.com/paug/AndroidMakersApp/sessions/d0e10687-71d2-4d05-9059-b43c6a78a48c
Co-authored-by: benju69 <2486590+benju69@users.noreply.github.com>
---
.../composeResources/values/strings.xml | 1 +
.../com/androidmakers/di/ViewModelModule.kt | 2 +-
.../ui/common/navigation/AVALayout.kt | 1 +
.../ui/speakers/SpeakerDetailViewModel.kt | 28 ++++++++-----
.../ui/speakers/SpeakerDetailsScreen.kt | 41 +++++++++++++++++++
5 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/shared/ui/src/commonMain/composeResources/values/strings.xml b/shared/ui/src/commonMain/composeResources/values/strings.xml
index fc875354..8be1f703 100644
--- a/shared/ui/src/commonMain/composeResources/values/strings.xml
+++ b/shared/ui/src/commonMain/composeResources/values/strings.xml
@@ -75,6 +75,7 @@
Apply for App-Clinic
Talk Details
+ Talks
About this talk
- Speaker
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/di/ViewModelModule.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/di/ViewModelModule.kt
index 94e105b4..015e82b4 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/di/ViewModelModule.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/di/ViewModelModule.kt
@@ -16,7 +16,7 @@ val viewModelModule = module {
viewModelOf(::SpeakerListViewModel)
viewModelOf(::SponsorsViewModel)
viewModelOf(::VenueViewModel)
- viewModel { (speakerId: String) -> SpeakerDetailsViewModel(speakerId, get()) }
+ viewModel { (speakerId: String) -> SpeakerDetailsViewModel(speakerId, get(), get()) }
viewModelOf(::AgendaViewModel)
viewModel { (sessionId: String) ->
SessionDetailViewModel(sessionId, get(), get(), get(), get(), get(), get(), get())
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
index f3ca4fa1..7b5a9121 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
@@ -410,6 +410,7 @@ private fun AVANavDisplay(
SpeakerDetailsRoute(
speakerDetailsViewModel = koinViewModel(key = key.speakerId) { parametersOf(key.speakerId) },
onBackClick = { navigator.goBack() },
+ onSessionClick = { sessionId -> navigator.navigateToSessionDetail(sessionId) },
sharedTransitionScope = sharedTransitionScope,
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
)
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailViewModel.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailViewModel.kt
index d59300bc..0305bb43 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailViewModel.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailViewModel.kt
@@ -4,28 +4,35 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.androidmakers.ui.model.Lce
import com.androidmakers.ui.model.toLce
+import fr.androidmakers.domain.model.Session
import fr.androidmakers.domain.model.SocialsItem
import fr.androidmakers.domain.model.Speaker
+import fr.androidmakers.domain.repo.SessionsRepository
import fr.androidmakers.domain.repo.SpeakersRepository
import fr.androidmakers.domain.utils.UrlOpener
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
class SpeakerDetailsViewModel(
speakerId: String,
speakersRepository: SpeakersRepository,
+ sessionsRepository: SessionsRepository,
) : ViewModel() {
- val uiState: StateFlow> = speakersRepository
- .getSpeaker(speakerId).map { result ->
- result.map {
- SpeakerDetailsUiState(
- speaker = it
- )
- }.toLce()
- }
+ val uiState: StateFlow> = combine(
+ speakersRepository.getSpeaker(speakerId),
+ sessionsRepository.getSessions(refresh = false),
+ ) { speakerResult, sessionsResult ->
+ speakerResult.map { speaker ->
+ SpeakerDetailsUiState(
+ speaker = speaker,
+ sessions = sessionsResult.getOrDefault(emptyList())
+ .filter { session -> speakerId in session.speakers }
+ )
+ }.toLce()
+ }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
@@ -38,5 +45,6 @@ class SpeakerDetailsViewModel(
}
data class SpeakerDetailsUiState(
- val speaker: Speaker
+ val speaker: Speaker,
+ val sessions: List = emptyList(),
)
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
index a812cc9d..14b16a02 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
@@ -5,12 +5,14 @@ import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -39,11 +41,14 @@ import com.androidmakers.ui.common.SocialButtons
import com.androidmakers.ui.common.toUrlOpener
import com.androidmakers.ui.model.Lce
import com.androidmakers.ui.theme.LocalIsNeobrutalism
+import com.androidmakers.ui.theme.neoBrutalElevation
+import fr.androidmakers.domain.model.Session
import fr.androidmakers.domain.model.SocialsItem
import fr.paug.androidmakers.ui.Res
import fr.paug.androidmakers.ui.ic_arrow_back
import fr.paug.androidmakers.ui.back
import fr.paug.androidmakers.ui.speakers
+import fr.paug.androidmakers.ui.talks
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@@ -52,6 +57,7 @@ import org.jetbrains.compose.resources.stringResource
fun SpeakerDetailsRoute(
speakerDetailsViewModel: SpeakerDetailsViewModel,
onBackClick: () -> Unit,
+ onSessionClick: (sessionId: String) -> Unit = {},
sharedTransitionScope: SharedTransitionScope? = null,
animatedVisibilityScope: AnimatedVisibilityScope? = null,
) {
@@ -68,6 +74,7 @@ fun SpeakerDetailsRoute(
uiState = state.content,
onSocialItemClick = { speakerDetailsViewModel.openSpeakerLink(urlOpener, it) },
onBackClick = onBackClick,
+ onSessionClick = onSessionClick,
sharedTransitionScope = sharedTransitionScope,
animatedVisibilityScope = animatedVisibilityScope,
)
@@ -81,6 +88,7 @@ fun SpeakerDetailsScreen(
uiState: SpeakerDetailsUiState,
onSocialItemClick: (SocialsItem) -> Unit,
onBackClick: () -> Unit,
+ onSessionClick: (sessionId: String) -> Unit = {},
sharedTransitionScope: SharedTransitionScope? = null,
animatedVisibilityScope: AnimatedVisibilityScope? = null,
) {
@@ -162,6 +170,39 @@ fun SpeakerDetailsScreen(
speaker = speaker,
onClickOnItem = onSocialItemClick
)
+
+ if (uiState.sessions.isNotEmpty()) {
+ SpeakerTalksSection(
+ sessions = uiState.sessions,
+ onSessionClick = onSessionClick,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun SpeakerTalksSection(
+ sessions: List,
+ onSessionClick: (sessionId: String) -> Unit,
+) {
+ Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ Text(
+ text = stringResource(Res.string.talks),
+ style = MaterialTheme.typography.titleMedium,
+ )
+ for (session in sessions) {
+ ElevatedCard(
+ modifier = Modifier.fillMaxWidth().neoBrutalElevation(),
+ onClick = { onSessionClick(session.id) },
+ shape = MaterialTheme.shapes.large,
+ ) {
+ Text(
+ modifier = Modifier.padding(16.dp),
+ text = session.title,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
}
}
}
From 0fa3b8fbb11335c7a2358ff279842d3f37d4a406 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 8 Apr 2026 19:39:58 +0000
Subject: [PATCH 2/3] Use forEach instead of for loop in Compose composable
Agent-Logs-Url: https://github.com/paug/AndroidMakersApp/sessions/d0e10687-71d2-4d05-9059-b43c6a78a48c
Co-authored-by: benju69 <2486590+benju69@users.noreply.github.com>
---
.../com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
index 14b16a02..d4a886d2 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
@@ -191,7 +191,7 @@ private fun SpeakerTalksSection(
text = stringResource(Res.string.talks),
style = MaterialTheme.typography.titleMedium,
)
- for (session in sessions) {
+ sessions.forEach { session ->
ElevatedCard(
modifier = Modifier.fillMaxWidth().neoBrutalElevation(),
onClick = { onSessionClick(session.id) },
From 2971f57a1dacd6135459461dd6ec9aee6ade2909 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 8 Apr 2026 19:47:48 +0000
Subject: [PATCH 3/3] Add Compose previews to SpeakerDetailsScreen
Agent-Logs-Url: https://github.com/paug/AndroidMakersApp/sessions/8ad1b52f-988f-42e0-8afb-91a6dfad042d
Co-authored-by: benju69 <2486590+benju69@users.noreply.github.com>
---
.../ui/speakers/SpeakerDetailsScreen.kt | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
index d4a886d2..f60869ba 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerDetailsScreen.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -44,11 +45,13 @@ import com.androidmakers.ui.theme.LocalIsNeobrutalism
import com.androidmakers.ui.theme.neoBrutalElevation
import fr.androidmakers.domain.model.Session
import fr.androidmakers.domain.model.SocialsItem
+import fr.androidmakers.domain.model.Speaker
import fr.paug.androidmakers.ui.Res
import fr.paug.androidmakers.ui.ic_arrow_back
import fr.paug.androidmakers.ui.back
import fr.paug.androidmakers.ui.speakers
import fr.paug.androidmakers.ui.talks
+import kotlinx.datetime.LocalDateTime
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@@ -206,3 +209,69 @@ private fun SpeakerTalksSection(
}
}
}
+
+// region Previews
+
+private val previewSpeaker = Speaker(
+ id = "speaker-1",
+ name = "Ada Lovelace",
+ company = "Babbage & Co.",
+ bio = "Mathematician and writer, chiefly known for her work on Charles Babbage's proposed mechanical general-purpose computer, the Analytical Engine.",
+ photoUrl = null,
+ socials = listOf(
+ SocialsItem(name = "Twitter", url = "https://twitter.com/ada"),
+ ),
+)
+
+private val previewSessions = listOf(
+ Session(
+ id = "session-1",
+ title = "The First Algorithm: A Deep Dive into Analytical Engine Programming",
+ speakers = listOf("speaker-1"),
+ startsAt = LocalDateTime(2026, 4, 9, 9, 0),
+ endsAt = LocalDateTime(2026, 4, 9, 9, 45),
+ roomId = "room-1",
+ isServiceSession = false,
+ type = "talk",
+ ),
+ Session(
+ id = "session-2",
+ title = "Beyond Numbers: Mathematical Imagination in the 19th Century",
+ speakers = listOf("speaker-1"),
+ startsAt = LocalDateTime(2026, 4, 10, 11, 0),
+ endsAt = LocalDateTime(2026, 4, 10, 11, 45),
+ roomId = "room-2",
+ isServiceSession = false,
+ type = "talk",
+ ),
+)
+
+@Preview
+@Composable
+private fun SpeakerDetailsScreenPreview() {
+ SpeakerDetailsScreen(
+ uiState = SpeakerDetailsUiState(
+ speaker = previewSpeaker,
+ sessions = previewSessions,
+ ),
+ onSocialItemClick = {},
+ onBackClick = {},
+ onSessionClick = {},
+ )
+}
+
+@Preview
+@Composable
+private fun SpeakerDetailsScreenNoTalksPreview() {
+ SpeakerDetailsScreen(
+ uiState = SpeakerDetailsUiState(
+ speaker = previewSpeaker,
+ sessions = emptyList(),
+ ),
+ onSocialItemClick = {},
+ onBackClick = {},
+ onSessionClick = {},
+ )
+}
+
+// endregion