diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 3fc144b..9c8c42f 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -65,6 +65,7 @@ kotlin {
implementation(libs.coil.network)
implementation(libs.coil.compose)
+ implementation(libs.coil.svg)
}
appleMain.dependencies {
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 3e3528f..4957c15 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -16,5 +16,11 @@
Evento registrado con éxito
Ocurrió un error al registrar el evento
Añadir al calendario
+
+
+
+ Organizers
+ Ex Organizers
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/App.kt b/composeApp/src/commonMain/kotlin/App.kt
index c72108f..c9e2bfd 100644
--- a/composeApp/src/commonMain/kotlin/App.kt
+++ b/composeApp/src/commonMain/kotlin/App.kt
@@ -14,6 +14,7 @@ import androidx.navigation.navArgument
import core.ui.AdpDestination
import core.ui.AdpTheme
import core.ui.components.AdpBottomNavigationBar
+import features.community.CommunityDetailScreen
import features.eventDetail.EventDetailScreen
import features.main.HomeRoute
@@ -32,9 +33,9 @@ fun App() {
}
},
onLiveEventTap = {
- navController.navigate(AdpDestination.LiveEvent.route) {
+ /*navController.navigate(AdpDestination.LiveEvent.route) {
this.launchSingleTop = true
- }
+ }*/
},
onInfoTap = {
navController.navigate(AdpDestination.CommunityDetails.route) {
@@ -67,10 +68,9 @@ fun App() {
EventDetailScreen()
}
composable(
- route = AdpDestination.CommunityDetails.route,
- arguments = listOf(navArgument("eventId", builder = { type = StringType }))
+ route = AdpDestination.CommunityDetails.route
) {
- // CommunityDetailScreen()
+ CommunityDetailScreen()
}
composable(
route = AdpDestination.LiveEvent.route,
diff --git a/composeApp/src/commonMain/kotlin/data/repository/MockCommunityRepository.kt b/composeApp/src/commonMain/kotlin/data/repository/MockCommunityRepository.kt
index c99caad..c50c7cf 100644
--- a/composeApp/src/commonMain/kotlin/data/repository/MockCommunityRepository.kt
+++ b/composeApp/src/commonMain/kotlin/data/repository/MockCommunityRepository.kt
@@ -49,7 +49,7 @@ class MockCommunityRepository(
url = "https://www.instagram.com/androiddevperu/"
),
SocialMedia(
- icon = "https://cdn.simpleicons.org/linkedin",
+ icon = "https://cdn-icons-png.flaticon.com/512/174/174857.png",
url = "https://www.linkedin.com/company/android-dev-peru"
),
SocialMedia(
diff --git a/composeApp/src/commonMain/kotlin/features/community/CommunityDetailScreen.kt b/composeApp/src/commonMain/kotlin/features/community/CommunityDetailScreen.kt
index 1f5ec02..ac5fb31 100644
--- a/composeApp/src/commonMain/kotlin/features/community/CommunityDetailScreen.kt
+++ b/composeApp/src/commonMain/kotlin/features/community/CommunityDetailScreen.kt
@@ -1,2 +1,225 @@
package features.community
+import adpmeetups.composeapp.generated.resources.Res
+import adpmeetups.composeapp.generated.resources.communityTitle_ex_organizer
+import adpmeetups.composeapp.generated.resources.communityTitle_organizer
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.isSystemInDarkTheme
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+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.graphics.ColorFilter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.platform.UriHandler
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import coil3.compose.AsyncImage
+import coil3.compose.LocalPlatformContext
+import coil3.request.ImageRequest
+import coil3.svg.SvgDecoder
+import core.DomainInjector
+import domain.models.Community
+import domain.models.Organizer
+import domain.models.SocialMedia
+import org.jetbrains.compose.resources.stringResource
+
+@Composable
+fun CommunityDetailScreen(
+ vm: CommunityDetailViewModel = viewModel {
+ CommunityDetailViewModel(
+ DomainInjector.getCommunityInfo
+ )
+ }
+) {
+
+ val uiState by vm.uiState.collectAsState()
+ val uriHandler = LocalUriHandler.current
+
+ uiState.community?.let {
+ CommunityDetailLayout(
+ modifier = Modifier.background(MaterialTheme.colors.background),
+ community = it,
+ organizers = it.organizers,
+ exOrganizers = it.exOrganizers,
+ socialMedia = it.socialMedia,
+ uriHandler = uriHandler
+ )
+ }
+}
+
+@Composable
+private fun CommunityDetailLayout(
+ modifier: Modifier = Modifier,
+ community: Community?,
+ organizers: List,
+ exOrganizers: List,
+ socialMedia: List,
+ uriHandler: UriHandler
+) {
+ LazyColumn (
+ modifier = modifier.fillMaxSize()
+ ) {
+ item {
+ AsyncImage(
+ modifier = Modifier
+ .fillMaxWidth(),
+ contentScale = ContentScale.FillWidth,
+ model= community?.topBanner,
+ contentDescription = community?.name,
+ )
+
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ Text(
+ text = community?.name.orEmpty(),
+ style = MaterialTheme.typography.h5
+ )
+
+ Spacer(modifier = Modifier.size(8.dp))
+
+ Text(text = community?.description.orEmpty())
+ }
+
+ Spacer(modifier = Modifier.size(8.dp))
+ }
+
+ item {
+ Text(
+ modifier = Modifier.padding(16.dp),
+ text = stringResource(Res.string.communityTitle_organizer)
+ )
+ }
+
+ organizers.forEach { organizer ->
+ item {
+ OrganizerItem(organizer = organizer)
+ Spacer(modifier = Modifier.height(16.dp))
+ }
+ }
+
+ item {
+ Text(
+ modifier = Modifier.padding(16.dp),
+ text = stringResource(Res.string.communityTitle_ex_organizer)
+ )
+ }
+
+ exOrganizers.forEach { exOrganizer ->
+ item {
+ OrganizerItem(organizer = exOrganizer)
+ Spacer(modifier = Modifier.height(16.dp))
+ }
+ }
+
+ item {
+ SocialMediaBar(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 12.dp),
+ socialMedia = socialMedia,
+ uriHandler = uriHandler
+ )
+ }
+ }
+}
+
+@Composable
+fun OrganizerItem(modifier: Modifier = Modifier, organizer: Organizer?) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ AsyncImage(
+ modifier = Modifier
+ .clip(androidx.compose.foundation.shape.CircleShape)
+ .size(80.dp),
+ contentScale = ContentScale.Crop,
+ model = organizer?.photo,
+ contentDescription = organizer?.name,
+ )
+
+ Text(
+ modifier = Modifier.padding(start = 16.dp),
+ text = organizer?.name.orEmpty(),
+ style = MaterialTheme.typography.subtitle1
+ )
+ }
+}
+
+@Composable
+fun SocialMediaBar(
+ modifier: Modifier = Modifier,
+ socialMedia: List,
+ uriHandler: UriHandler
+) {
+ LazyRow(
+ modifier = modifier.fillMaxWidth().padding(top = 16.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ contentPadding = PaddingValues(horizontal = 16.dp)
+ ) {
+ items(socialMedia.size) { social ->
+ SocialMediaItem(
+ socialMedia = socialMedia[social],
+ uriHandler = uriHandler
+ )
+ }
+ }
+}
+
+@Composable
+fun SocialMediaItem(
+ modifier: Modifier = Modifier,
+ socialMedia: SocialMedia,
+ uriHandler: UriHandler
+) {
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ AsyncImage(
+ modifier = Modifier
+ .clip(RoundedCornerShape(8.dp))
+ .size(24.dp)
+ .clickable {
+ uriHandler.openUri(socialMedia.url)
+ },
+ contentScale = ContentScale.Fit,
+ model = ImageRequest
+ .Builder(LocalPlatformContext.current)
+ .data(socialMedia.icon)
+ .decoderFactory(SvgDecoder.Factory())
+ .build(),
+ contentDescription = socialMedia.icon,
+ colorFilter = ColorFilter.tint(
+ if(isSystemInDarkTheme()) Color.White else Color.Black
+ )
+ )
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/features/community/CommunityDetailViewModel.kt b/composeApp/src/commonMain/kotlin/features/community/CommunityDetailViewModel.kt
index ed128ef..6f1e32c 100644
--- a/composeApp/src/commonMain/kotlin/features/community/CommunityDetailViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/features/community/CommunityDetailViewModel.kt
@@ -1,4 +1,48 @@
package features.community
-class CommunityDetailViewModel {
-}
\ No newline at end of file
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import domain.AdpError
+import domain.models.Community
+import domain.usecase.GetCommunityInfo
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+class CommunityDetailViewModel(
+ private val getCommunityInfo: GetCommunityInfo
+): ViewModel() {
+
+ private val _uiState = MutableStateFlow(CommunityDetailUiState())
+ val uiState: StateFlow = _uiState
+ .onStart {
+ getCommunityDetails()
+ }.stateIn(
+ viewModelScope,
+ SharingStarted.WhileSubscribed(),
+ CommunityDetailUiState()
+ )
+
+ private fun getCommunityDetails() {
+ // TODO add kotlinx-coroutines-swing to make viewModelScope
+ // available in desktop
+ viewModelScope.launch {
+ _uiState.update { it.copy(loading = true, error = null) }
+ getCommunityInfo.invoke().onSuccess { community ->
+ _uiState.update { it.copy(loading = false, community = community, error = null) }
+ }.onFailure {
+ _uiState.update { it.copy(loading = false, error = null) }
+ }
+ }
+ }
+}
+
+data class CommunityDetailUiState(
+ val loading: Boolean = true,
+ val community: Community? = null,
+ val error: AdpError? = null
+)
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4c19e62..a86fdbd 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -50,6 +50,7 @@ koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
androidx-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "jetbrains-viewModel" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
coil-network = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
+coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }