diff --git a/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index 9880f48b7..000000000 Binary files a/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png and /dev/null differ diff --git a/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index 5aff5d3f3..000000000 Binary files a/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png and /dev/null differ diff --git a/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index 941957b89..000000000 Binary files a/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png and /dev/null differ diff --git a/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index 00d71c12a..000000000 Binary files a/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index daee47631..000000000 Binary files a/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index df811675b..000000000 Binary files a/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index 75938bd97..000000000 Binary files a/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index 39a44f434..000000000 Binary files a/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index dcc4efb27..000000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index fe8914263..000000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png and /dev/null differ diff --git a/app/src/androidHostTest/kotlin/com/divinelink/factories/api/DetailsResponseApiFactory.kt b/app/src/androidHostTest/kotlin/com/divinelink/factories/api/DetailsResponseApiFactory.kt index 3b13a8629..655170526 100644 --- a/app/src/androidHostTest/kotlin/com/divinelink/factories/api/DetailsResponseApiFactory.kt +++ b/app/src/androidHostTest/kotlin/com/divinelink/factories/api/DetailsResponseApiFactory.kt @@ -6,6 +6,7 @@ import com.divinelink.core.network.media.model.details.DetailsResponseApi import com.divinelink.core.network.media.model.details.ProductionCompany import com.divinelink.core.network.media.model.details.ProductionCountryResponse import com.divinelink.core.testing.factories.api.media.GenreResponseFactory +import com.divinelink.core.testing.factories.api.media.KeywordsResponseFactory import com.divinelink.factories.CreditsFactory object DetailsResponseApiFactory { @@ -84,5 +85,6 @@ object DetailsResponseApiFactory { voteAverage = 8.438, voteCount = 30_452, credits = CreditsFactory.all(), + keywords = KeywordsResponseFactory.fightClub, ) } diff --git a/app/src/androidHostTest/kotlin/com/divinelink/scenepeek/search/domain/repository/ProdDetailsRepositoryTest.kt b/app/src/androidHostTest/kotlin/com/divinelink/scenepeek/search/domain/repository/ProdDetailsRepositoryTest.kt index ee7846c10..7442c3873 100644 --- a/app/src/androidHostTest/kotlin/com/divinelink/scenepeek/search/domain/repository/ProdDetailsRepositoryTest.kt +++ b/app/src/androidHostTest/kotlin/com/divinelink/scenepeek/search/domain/repository/ProdDetailsRepositoryTest.kt @@ -198,7 +198,7 @@ class ProdDetailsRepositoryTest { request = MediaRequestApiFactory.tv(), ).test { assertThat(awaitItem()).isEqualTo( - Result.success(MediaDetailsFactory.TheOffice()), + Result.success(MediaDetailsFactory.TheOffice().copy(keywords = null)), ) awaitComplete() } diff --git a/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/details/media/DetailsDataFactory.kt b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/details/media/DetailsDataFactory.kt index e7fcf0700..28c24f727 100644 --- a/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/details/media/DetailsDataFactory.kt +++ b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/details/media/DetailsDataFactory.kt @@ -3,6 +3,7 @@ package com.divinelink.core.fixtures.details.media import com.divinelink.core.fixtures.details.credits.SeriesCastFactory import com.divinelink.core.fixtures.details.review.ReviewFactory import com.divinelink.core.fixtures.details.season.SeasonFactory +import com.divinelink.core.fixtures.model.details.KeywordFactory import com.divinelink.core.fixtures.model.details.MediaDetailsFactory import com.divinelink.core.fixtures.model.media.MediaItemFactory import com.divinelink.core.model.details.media.DetailsData @@ -17,6 +18,7 @@ object DetailsDataFactory { creators = null, information = null, collection = null, + keywords = null, ) fun cast(isTv: Boolean) = DetailsData.Cast( @@ -44,7 +46,8 @@ object DetailsDataFactory { genres = MediaDetailsFactory.FightClub().genres, creators = MediaDetailsFactory.FightClub().creators, information = MediaDetailsFactory.FightClub().information, - collection = null, // MediaDetailsFactory.FightClub().information + collection = null, + keywords = KeywordFactory.fightClub, ) fun cast() = DetailsData.Cast( @@ -69,6 +72,7 @@ object DetailsDataFactory { creators = MediaDetailsFactory.TheOffice().creators, information = MediaDetailsFactory.TheOffice().information, collection = null, + keywords = KeywordFactory.theOffice, ) fun cast() = DetailsData.Cast( diff --git a/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/KeywordFactory.kt b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/KeywordFactory.kt new file mode 100644 index 000000000..c5d32be84 --- /dev/null +++ b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/KeywordFactory.kt @@ -0,0 +1,42 @@ +package com.divinelink.core.fixtures.model.details + +import com.divinelink.core.model.details.Keyword + +object KeywordFactory { + + val basedOnNovelOrBook = Keyword( + id = 818, + name = "based on novel or book", + ) + + val fight = Keyword( + id = 1721, + name = "fight", + ) + + val mockumentary = Keyword( + id = 11800, + name = "Mockumentary", + ) + + val amused = Keyword( + id = 325765, + name = "Amused", + ) + + val sitcom = Keyword( + id = 193171, + name = "sitcom", + ) + + val theOffice = listOf( + mockumentary, + amused, + sitcom, + ) + + val fightClub = listOf( + basedOnNovelOrBook, + fight, + ) +} diff --git a/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/MediaDetailsFactory.kt b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/MediaDetailsFactory.kt index e747233b9..aac7606a1 100644 --- a/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/MediaDetailsFactory.kt +++ b/core/fixtures/src/commonMain/kotlin/com/divinelink/core/fixtures/model/details/MediaDetailsFactory.kt @@ -66,6 +66,7 @@ object MediaDetailsFactory { Country.UNITED_STATES, ), ), + keywords = KeywordFactory.fightClub, ) fun TheOffice() = TV( @@ -130,5 +131,6 @@ object MediaDetailsFactory { ), nextEpisodeAirDate = null, ), + keywords = KeywordFactory.theOffice, ) } diff --git a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Keyword.kt b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Keyword.kt new file mode 100644 index 000000000..5ae0e0be4 --- /dev/null +++ b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Keyword.kt @@ -0,0 +1,6 @@ +package com.divinelink.core.model.details + +data class Keyword( + val id: Long, + val name: String, +) diff --git a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/MediaDetails.kt b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/MediaDetails.kt index 51b4ab6ed..e0c093f0a 100644 --- a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/MediaDetails.kt +++ b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/MediaDetails.kt @@ -24,6 +24,7 @@ sealed class MediaDetails { abstract val imdbId: String? abstract val popularity: Double abstract val information: MediaDetailsInformation + abstract val keywords: List? fun copy( id: Int = this.id, @@ -56,6 +57,7 @@ sealed class MediaDetails { popularity = popularity, collection = collection, information = information, + keywords = keywords, ) is TV -> TV( id = id, @@ -74,6 +76,7 @@ sealed class MediaDetails { imdbId = imdbId, popularity = popularity, information = information, + keywords = keywords, ) } } diff --git a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Movie.kt b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Movie.kt index 5b586f9e1..81cf559c2 100644 --- a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Movie.kt +++ b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/Movie.kt @@ -25,4 +25,5 @@ data class Movie( override val information: MediaDetailsInformation.Movie, override val popularity: Double, override val isFavorite: Boolean, + override val keywords: List?, ) : MediaDetails() diff --git a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/TV.kt b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/TV.kt index 62a3b3a3b..13d642cbc 100644 --- a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/TV.kt +++ b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/TV.kt @@ -21,6 +21,7 @@ data class TV( override val tagline: String?, override val popularity: Double, override val information: MediaDetailsInformation.TV, + override val keywords: List?, val creators: List?, val seasons: List, val numberOfSeasons: Int, diff --git a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/media/DetailsData.kt b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/media/DetailsData.kt index 1255c3d8b..335105bed 100644 --- a/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/media/DetailsData.kt +++ b/core/model/src/commonMain/kotlin/com/divinelink/core/model/details/media/DetailsData.kt @@ -2,6 +2,7 @@ package com.divinelink.core.model.details.media import com.divinelink.core.model.Genre import com.divinelink.core.model.details.Collection +import com.divinelink.core.model.details.Keyword import com.divinelink.core.model.details.Person import com.divinelink.core.model.details.Season import com.divinelink.core.model.details.review.Review @@ -15,6 +16,7 @@ sealed interface DetailsData { val creators: List?, val information: MediaDetailsInformation?, val collection: Collection?, + val keywords: List?, ) : DetailsData data class Cast( diff --git a/core/network/src/androidHostTest/kotlin/com/divinelink/core/network/media/util/BuildUrlTest.kt b/core/network/src/androidHostTest/kotlin/com/divinelink/core/network/media/util/BuildUrlTest.kt index dfd5b119b..8a718bcc4 100644 --- a/core/network/src/androidHostTest/kotlin/com/divinelink/core/network/media/util/BuildUrlTest.kt +++ b/core/network/src/androidHostTest/kotlin/com/divinelink/core/network/media/util/BuildUrlTest.kt @@ -15,8 +15,9 @@ class BuildUrlTest { appendToResponse = true, ) - url shouldBe - "https://api.themoviedb.org/3/tv/1234?language=en-US&append_to_response=external_ids" + url shouldBe "https://api.themoviedb.org/3/tv/1234" + + "?language=en-US" + + "&append_to_response=external_ids%2Ckeywords" } @Test @@ -38,7 +39,9 @@ class BuildUrlTest { appendToResponse = true, ) - url shouldBe "https://api.themoviedb.org/3/movie/1234?language=en-US&append_to_response=credits" + url shouldBe "https://api.themoviedb.org/3/movie/1234" + + "?language=en-US" + + "&append_to_response=credits%2Ckeywords" } @Test diff --git a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/mapper/details/KeywordsResponseMapper.kt b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/mapper/details/KeywordsResponseMapper.kt new file mode 100644 index 000000000..b7348a296 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/mapper/details/KeywordsResponseMapper.kt @@ -0,0 +1,14 @@ +package com.divinelink.core.network.media.mapper.details + +import com.divinelink.core.model.details.Keyword +import com.divinelink.core.network.media.model.details.KeywordResponse +import com.divinelink.core.network.media.model.details.KeywordsResponse + +fun KeywordsResponse?.map() = this + ?.keywords + ?.map { it.map() } + +private fun KeywordResponse.map() = Keyword( + id = id, + name = name, +) diff --git a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/DetailsResponseApi.kt b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/DetailsResponseApi.kt index 7895a4d0e..7adbf1d60 100644 --- a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/DetailsResponseApi.kt +++ b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/DetailsResponseApi.kt @@ -54,6 +54,8 @@ sealed class DetailsResponseApi { @SerialName("production_countries") abstract val countries: List + abstract val keywords: KeywordsResponse? + @Serializable data class Movie( override val id: Int, @@ -82,6 +84,7 @@ sealed class DetailsResponseApi { val credits: CreditsApi? = null, // TODO credits call should be made separately val status: String? = null, @SerialName("belongs_to_collection") val collection: BelongsToCollectionResponse?, + override val keywords: KeywordsResponse?, ) : DetailsResponseApi() @Serializable @@ -111,6 +114,7 @@ sealed class DetailsResponseApi { @SerialName("next_episode_to_air") val nextEpisodeToAir: NextEpisodeToAirResponse? = null, @SerialName("production_companies") override val companies: List, @SerialName("production_countries") override val countries: List, + override val keywords: KeywordsResponse?, ) : DetailsResponseApi() } @@ -159,6 +163,7 @@ private fun DetailsResponseApi.Movie.toDomainMovie(): MediaDetails = Movie( "$${revenue.formatWithCommas()}" }, ), + keywords = keywords.map(), ) private fun DetailsResponseApi.TV.toDomainTVShow(): MediaDetails = TV( @@ -194,6 +199,7 @@ private fun DetailsResponseApi.TV.toDomainTVShow(): MediaDetails = TV( Country.fromCode(it.iso31611) }, ), + keywords = keywords.map(), ) private fun List.toActors(): List = this.map(CastApi::toPerson) diff --git a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/KeywordResponse.kt b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/KeywordResponse.kt new file mode 100644 index 000000000..35c081fea --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/model/details/KeywordResponse.kt @@ -0,0 +1,17 @@ +package com.divinelink.core.network.media.model.details + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonNames + +@Serializable +data class KeywordResponse( + val id: Long, + val name: String, +) + +@OptIn(ExperimentalSerializationApi::class) +@Serializable +data class KeywordsResponse( + @JsonNames("results", "keywords") val keywords: List, +) diff --git a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/util/BuildUrl.kt b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/util/BuildUrl.kt index 1ce0a1e97..86a224b55 100644 --- a/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/util/BuildUrl.kt +++ b/core/network/src/commonMain/kotlin/com/divinelink/core/network/media/util/BuildUrl.kt @@ -22,11 +22,9 @@ fun buildFetchDetailsUrl( append("language", "en-US") if (appendToResponse) { when (media) { - MediaType.MOVIE -> append("append_to_response", "credits") - MediaType.TV -> append("append_to_response", "external_ids") - else -> { - // Do nothing - } + MediaType.MOVIE -> append("append_to_response", "credits,keywords") + MediaType.TV -> append("append_to_response", "external_ids,keywords") + else -> Unit } } } diff --git a/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordResponseFactory.kt b/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordResponseFactory.kt new file mode 100644 index 000000000..efc7abdea --- /dev/null +++ b/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordResponseFactory.kt @@ -0,0 +1,42 @@ +package com.divinelink.core.testing.factories.api.media + +import com.divinelink.core.network.media.model.details.KeywordResponse + +object KeywordResponseFactory { + + val basedOnNovelOrBook = KeywordResponse( + id = 818, + name = "based on novel or book", + ) + + val fight = KeywordResponse( + id = 1721, + name = "fight", + ) + + val mockumentary = KeywordResponse( + id = 11800, + name = "Mockumentary", + ) + + val amused = KeywordResponse( + id = 325765, + name = "Amused", + ) + + val sitcom = KeywordResponse( + id = 193171, + name = "sitcom", + ) + + val theOffice = listOf( + mockumentary, + amused, + sitcom, + ) + + val fightClub = listOf( + basedOnNovelOrBook, + fight, + ) +} diff --git a/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordsResponseFactory.kt b/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordsResponseFactory.kt new file mode 100644 index 000000000..133f6024c --- /dev/null +++ b/core/testing/src/commonMain/kotlin/com/divinelink/core/testing/factories/api/media/KeywordsResponseFactory.kt @@ -0,0 +1,9 @@ +package com.divinelink.core.testing.factories.api.media + +import com.divinelink.core.network.media.model.details.KeywordsResponse + +object KeywordsResponseFactory { + + val theOffice = KeywordsResponse(keywords = KeywordResponseFactory.theOffice) + val fightClub = KeywordsResponse(keywords = KeywordResponseFactory.fightClub) +} diff --git a/core/ui/src/commonMain/composeResources/values/strings.xml b/core/ui/src/commonMain/composeResources/values/strings.xml index 28621298f..2b2c54f65 100644 --- a/core/ui/src/commonMain/composeResources/values/strings.xml +++ b/core/ui/src/commonMain/composeResources/values/strings.xml @@ -7,6 +7,7 @@ Share error details Information + Keywords Approve Decline diff --git a/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/cast/CreatorsItem.kt b/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/cast/CreatorsItem.kt index 95600443c..b05009c02 100644 --- a/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/cast/CreatorsItem.kt +++ b/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/cast/CreatorsItem.kt @@ -24,11 +24,12 @@ import org.jetbrains.compose.resources.stringResource @Composable fun CreatorsItem( + modifier: Modifier = Modifier, creators: List, onClick: (Person) -> Unit, ) { FlowRow( - modifier = Modifier.offset(x = -MaterialTheme.dimensions.keyline_12), + modifier = modifier.offset(x = -MaterialTheme.dimensions.keyline_12), ) { creators.forEach { person -> CreatorItem( diff --git a/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/keywords/KeywordLabel.kt b/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/keywords/KeywordLabel.kt new file mode 100644 index 000000000..e57afaa81 --- /dev/null +++ b/core/ui/src/commonMain/kotlin/com/divinelink/core/ui/components/details/keywords/KeywordLabel.kt @@ -0,0 +1,49 @@ +package com.divinelink.core.ui.components.details.keywords + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import com.divinelink.core.designsystem.theme.dimensions +import com.divinelink.core.model.details.Keyword + +@Composable +fun KeywordLabel( + modifier: Modifier = Modifier, + keyword: Keyword, + onClick: (Keyword) -> Unit, +) { + Surface( + shape = MaterialTheme.shapes.small, + modifier = modifier + .wrapContentSize(Alignment.Center) + .wrapContentHeight() + .clip(MaterialTheme.shapes.small) + .clickable(onClick = { onClick(keyword) }), + ) { + Box { + Text( + modifier = Modifier + .align(Alignment.Center) + .padding( + horizontal = MaterialTheme.dimensions.keyline_8, + vertical = MaterialTheme.dimensions.keyline_8, + ), + textAlign = TextAlign.Center, + text = keyword.name, + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold, + ) + } + } +} diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/DetailsViewModel.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/DetailsViewModel.kt index a65d312ce..4b299218b 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/DetailsViewModel.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/DetailsViewModel.kt @@ -801,6 +801,7 @@ class DetailsViewModel( }, information = result.mediaDetails.information, collection = (result.mediaDetails as? Movie)?.collection, + keywords = result.mediaDetails.keywords, ) /** diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/GenresSection.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/GenresSection.kt index db8051c6e..370956847 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/GenresSection.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/GenresSection.kt @@ -17,10 +17,11 @@ import org.jetbrains.compose.resources.stringResource @Composable fun GenresSection( + modifier: Modifier = Modifier, genres: List, onGenreClick: (Genre) -> Unit, ) { - Column { + Column(modifier = modifier) { Text( modifier = Modifier.padding(bottom = MaterialTheme.dimensions.keyline_8), text = stringResource(Res.string.feature_details_genres), diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/KeywordsSection.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/KeywordsSection.kt new file mode 100644 index 000000000..dbcc4a461 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/KeywordsSection.kt @@ -0,0 +1,42 @@ +package com.divinelink.feature.details.media.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.divinelink.core.designsystem.theme.dimensions +import com.divinelink.core.model.details.Keyword +import com.divinelink.core.ui.UiString +import com.divinelink.core.ui.components.details.keywords.KeywordLabel +import com.divinelink.core.ui.resources.core_ui_keywords +import org.jetbrains.compose.resources.stringResource + +@Composable +fun KeywordsSection( + keywords: List, + onClick: (Keyword) -> Unit, +) { + Column { + Text( + modifier = Modifier.padding( + start = MaterialTheme.dimensions.keyline_16, + bottom = MaterialTheme.dimensions.keyline_8, + ), + text = stringResource(UiString.core_ui_keywords), + style = MaterialTheme.typography.titleMedium, + ) + FlowRow( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_8), + ) { + keywords.forEach { keyword -> + KeywordLabel( + keyword = keyword, + onClick = onClick, + ) + } + } + } +} diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/MovieInformationSection.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/MovieInformationSection.kt index 8a2588041..66946733c 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/MovieInformationSection.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/MovieInformationSection.kt @@ -28,9 +28,12 @@ import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource @Composable -fun MovieInformationSection(information: MediaDetailsInformation.Movie) { +fun MovieInformationSection( + modifier: Modifier = Modifier, + information: MediaDetailsInformation.Movie, +) { Column( - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(MaterialTheme.dimensions.keyline_16), ) { Text( diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/TvInformationSection.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/TvInformationSection.kt index 42f748ca3..aade4dbf6 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/TvInformationSection.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/components/TvInformationSection.kt @@ -26,9 +26,12 @@ import com.divinelink.feature.details.resources.feature_details_information_stat import org.jetbrains.compose.resources.stringResource @Composable -fun TvInformationSection(information: MediaDetailsInformation.TV) { +fun TvInformationSection( + modifier: Modifier = Modifier, + information: MediaDetailsInformation.TV, +) { Column( - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(MaterialTheme.dimensions.keyline_16), ) { Text( diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/AboutFormContent.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/AboutFormContent.kt index 57c34ca57..c77def78e 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/AboutFormContent.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/AboutFormContent.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -21,6 +22,7 @@ import com.divinelink.core.navigation.route.toPersonRoute import com.divinelink.core.ui.TestTags import com.divinelink.core.ui.components.details.cast.CreatorsItem import com.divinelink.feature.details.media.ui.components.GenresSection +import com.divinelink.feature.details.media.ui.components.KeywordsSection import com.divinelink.feature.details.media.ui.components.MovieInformationSection import com.divinelink.feature.details.media.ui.components.TvInformationSection @@ -32,16 +34,13 @@ fun AboutFormContent( ) { ScenePeekLazyColumn( modifier = modifier.testTag(TestTags.Details.About.FORM), - contentPadding = PaddingValues( - top = MaterialTheme.dimensions.keyline_16, - start = MaterialTheme.dimensions.keyline_16, - end = MaterialTheme.dimensions.keyline_16, - ), + contentPadding = PaddingValues(top = MaterialTheme.dimensions.keyline_16), verticalArrangement = Arrangement.spacedBy(MaterialTheme.dimensions.keyline_16), ) { aboutData.tagline?.let { item { Text( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), text = it, style = MaterialTheme.typography.bodySmall, fontStyle = FontStyle.Italic, @@ -53,18 +52,22 @@ fun AboutFormContent( if (!aboutData.overview.isNullOrEmpty()) { item { Text( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), text = aboutData.overview!!, style = MaterialTheme.typography.bodyMedium, ) } item { - HorizontalDivider() + HorizontalDivider( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), + ) } } aboutData.genres?.let { genres -> item { GenresSection( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), genres = genres, onGenreClick = {}, ) @@ -74,6 +77,7 @@ fun AboutFormContent( aboutData.creators?.let { creators -> item { CreatorsItem( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), creators = creators, onClick = { onNavigate(it.toPersonRoute()) }, ) @@ -82,13 +86,21 @@ fun AboutFormContent( aboutData.information?.let { information -> item { - HorizontalDivider() + HorizontalDivider( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), + ) } item { when (information) { - is MediaDetailsInformation.Movie -> MovieInformationSection(information) - is MediaDetailsInformation.TV -> TvInformationSection(information) + is MediaDetailsInformation.Movie -> MovieInformationSection( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), + information = information, + ) + is MediaDetailsInformation.TV -> TvInformationSection( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), + information = information, + ) } } } @@ -96,6 +108,7 @@ fun AboutFormContent( aboutData.collection?.let { collection -> item { CollectionBanner( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), collection = collection, onClick = { onNavigate( @@ -111,6 +124,20 @@ fun AboutFormContent( } } + aboutData.keywords?.let { keywords -> + item { + HorizontalDivider( + modifier = Modifier.padding(horizontal = MaterialTheme.dimensions.keyline_16), + ) + } + item { + KeywordsSection( + keywords = keywords, + onClick = {}, + ) + } + } + item { Spacer(modifier = Modifier.height(LocalBottomNavigationPadding.current)) } diff --git a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/CollectionBanner.kt b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/CollectionBanner.kt index 0cd296eb4..a872266e3 100644 --- a/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/CollectionBanner.kt +++ b/feature/details/src/commonMain/kotlin/com/divinelink/feature/details/media/ui/forms/about/CollectionBanner.kt @@ -26,11 +26,12 @@ import org.jetbrains.compose.resources.stringResource @Composable fun CollectionBanner( + modifier: Modifier = Modifier, collection: Collection, onClick: () -> Unit, ) { Box( - modifier = Modifier + modifier = modifier .clip(MaterialTheme.shapes.medium) .clickable(onClick = onClick) .fillMaxWidth(),