diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8a0331c..caae2a6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.compose) @@ -21,6 +23,12 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + val properties = Properties() + properties.load(project.rootProject.file("local.properties").inputStream()) + + buildConfigField("String", "SUPABASE_URL", "\"${properties.getProperty("SUPABASE_URL")}\"") + buildConfigField("String", "SUPABASE_ANON_KEY", "\"${properties.getProperty("SUPABASE_ANON_KEY")}\"") } buildTypes { @@ -38,6 +46,7 @@ android { } buildFeatures { compose = true + buildConfig = true } } @@ -45,6 +54,7 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) + implementation(libs.androidx.activity.ktx) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.graphics) @@ -75,6 +85,11 @@ dependencies { implementation(libs.coil.compose) implementation(libs.coil.network.okhttp) + // Supabase + implementation(platform(libs.bom)) + implementation(libs.postgrest.kt) + implementation(libs.ktor.client.android) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6d91e7b..e133306 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ { GundamdexHomeViewModel.Factory } + setContent { GundamdexAppTheme { - GundamdexNavigation() + GundamdexNavigation( + gundamdexHomeViewModel = gundamdexHomeViewModel, + ) } } } diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/AppContainer.kt b/app/src/main/java/com/example/gundamdexapp/data/network/AppContainer.kt new file mode 100644 index 0000000..872a22a --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/AppContainer.kt @@ -0,0 +1,10 @@ +package com.example.gundamdexapp.data.network + +import com.example.gundamdexapp.data.network.repositoryImpl.GundamRepositoryImpl +import com.example.gundamdexapp.domain.repository.GundamRepository + +class AppContainer { + val gundamRepository: GundamRepository by lazy { + GundamRepositoryImpl(SupabaseNetwork.gundamApi) + } +} diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/SupabaseNetwork.kt b/app/src/main/java/com/example/gundamdexapp/data/network/SupabaseNetwork.kt new file mode 100644 index 0000000..078be2c --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/SupabaseNetwork.kt @@ -0,0 +1,33 @@ +package com.example.gundamdexapp.data.network + +import com.example.gundamdexapp.BuildConfig +import com.example.gundamdexapp.data.network.api.GundamApi +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory + +object SupabaseNetwork { + // Logcat에서 통신 로그를 보기 위한 설정 + private val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + private val okHttpClient = OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .build() + + private val json = Json { + ignoreUnknownKeys = true + } + + private val retrofit = Retrofit.Builder() + .baseUrl("${BuildConfig.SUPABASE_URL}/") + .client(okHttpClient) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + + val gundamApi: GundamApi = retrofit.create(GundamApi::class.java) +} diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/api/GundamApi.kt b/app/src/main/java/com/example/gundamdexapp/data/network/api/GundamApi.kt new file mode 100644 index 0000000..62acdd0 --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/api/GundamApi.kt @@ -0,0 +1,25 @@ +package com.example.gundamdexapp.data.network.api + +import com.example.gundamdexapp.BuildConfig +import com.example.gundamdexapp.data.network.dto.GundamDto +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Query + +interface GundamApi { + @GET("rest/v1/gundams") + suspend fun getGundams( + @Header("apikey") apikey: String = BuildConfig.SUPABASE_ANON_KEY, + @Header("Authorization") auth: String = "Bearer ${BuildConfig.SUPABASE_ANON_KEY}", + @Query("select") selectQuery: String = "*, armaments(*)", + ): Response> + + @GET("rest/v1/gundams") + suspend fun getGundamDetail( + @Header("apikey") apikey: String = BuildConfig.SUPABASE_ANON_KEY, + @Header("Authorization") auth: String = "Bearer ${BuildConfig.SUPABASE_ANON_KEY}", + @Query("id") idFilter: String, + @Query("select") selectQuery: String = "*, armaments(*)", + ): Response> +} diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/dto/ArmamentDto.kt b/app/src/main/java/com/example/gundamdexapp/data/network/dto/ArmamentDto.kt new file mode 100644 index 0000000..75e2e3c --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/dto/ArmamentDto.kt @@ -0,0 +1,21 @@ +package com.example.gundamdexapp.data.network.dto + +import com.example.gundamdexapp.domain.model.Armament +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ArmamentDto( + @SerialName("gundam_id") + val gundamId: String, + val name: String, + val details: String? = null, + @SerialName("indicator_color") + val indicatorColor: String, +) { + fun toDomain(): Armament = Armament( + name = this.name, + details = this.details ?: "", + indicatorColor = this.indicatorColor, + ) +} diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/dto/GundamDto.kt b/app/src/main/java/com/example/gundamdexapp/data/network/dto/GundamDto.kt new file mode 100644 index 0000000..d1cda74 --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/dto/GundamDto.kt @@ -0,0 +1,68 @@ +package com.example.gundamdexapp.data.network.dto + +import com.example.gundamdexapp.domain.model.Armaments +import com.example.gundamdexapp.domain.model.Dimensions +import com.example.gundamdexapp.domain.model.GundamInfo +import com.example.gundamdexapp.domain.model.TechnicalSpecifications +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GundamDto( + val id: String, + @SerialName("model_number") + val modelNumber: String? = null, + val name: String, + val series: String, + @SerialName("image_url") + val imageUrl: String? = null, + val era: String, + val faction: String, + val pilot: String? = null, + val description: String, + @SerialName("dim_weight") + val weight: String? = null, + @SerialName("dim_height") + val height: String? = null, + @SerialName("tech_generator_output") + val generatorOutput: String? = null, + @SerialName("tech_armor_material") + val armorMaterial: String? = null, + @SerialName("tech_total_trust") + val totalTrust: String? = null, + @SerialName("tech_sensor_radius") + val sensorRadius: String? = null, + @SerialName("tech_crew") + val crew: String? = null, + val armaments: List = emptyList(), +) { + fun toDomain(): GundamInfo = GundamInfo( + id = this.id, + modelNumber = this.modelNumber ?: MISSING_TEXT, + name = this.name, + series = this.series, + imageUrl = this.imageUrl ?: MISSING_URL, + era = this.era, + faction = this.faction, + pilot = this.pilot ?: MISSING_TEXT, + description = this.description, + dimensions = Dimensions( + height = this.height ?: MISSING_TEXT, + weight = this.weight ?: MISSING_TEXT, + ), + technicalSpecifications = TechnicalSpecifications( + generatorOutput = this.generatorOutput ?: MISSING_TEXT, + armorMaterial = this.armorMaterial ?: MISSING_TEXT, + totalTrust = this.totalTrust ?: MISSING_TEXT, + sensorRadius = this.sensorRadius ?: MISSING_TEXT, + crew = this.crew ?: MISSING_TEXT, + ), + armaments = Armaments(this.armaments.map { it.toDomain() }), + ) + + companion object { + const val MISSING_TEXT = "[ ? ]" + const val MISSING_URL = + "https://imgfiles-cdn.plaync.com/file/BladeNSoul/download/20190802085052-S4BOYrvsgcmithzSaShj0-v4" + } +} diff --git a/app/src/main/java/com/example/gundamdexapp/data/network/repositoryImpl/GundamRepositoryImpl.kt b/app/src/main/java/com/example/gundamdexapp/data/network/repositoryImpl/GundamRepositoryImpl.kt new file mode 100644 index 0000000..db0e60f --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/data/network/repositoryImpl/GundamRepositoryImpl.kt @@ -0,0 +1,39 @@ +package com.example.gundamdexapp.data.network.repositoryImpl + +import com.example.gundamdexapp.data.network.api.GundamApi +import com.example.gundamdexapp.domain.model.GundamInfo +import com.example.gundamdexapp.domain.repository.GundamRepository + +class GundamRepositoryImpl( + private val api: GundamApi, +) : GundamRepository { + override suspend fun getGundamList(): Result> = try { + val response = api.getGundams() + + if (response.isSuccessful) { + val dtoList = response.body() ?: emptyList() + + val domainList = dtoList.map { it.toDomain() } + Result.success(domainList) + } else { + Result.failure(Exception("서버 통신 실패 : ${response.code()}")) + } + } catch (e: Exception) { + Result.failure(e) + } + + override suspend fun getGundamDetail(id: String): Result> = try { + val response = api.getGundamDetail(idFilter = "eq.$id") + + if (response.isSuccessful) { + val gundamDto = response.body() ?: emptyList() + + val domainList = gundamDto.map { it.toDomain() } + Result.success(domainList) + } else { + Result.failure(Exception("서버 통신 실패 : ${response.code()}")) + } + } catch (e: Exception) { + Result.failure(e) + } +} diff --git a/app/src/main/java/com/example/gundamdexapp/domain/repository/GundamRepository.kt b/app/src/main/java/com/example/gundamdexapp/domain/repository/GundamRepository.kt new file mode 100644 index 0000000..23dc82d --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/domain/repository/GundamRepository.kt @@ -0,0 +1,8 @@ +package com.example.gundamdexapp.domain.repository + +import com.example.gundamdexapp.domain.model.GundamInfo + +interface GundamRepository { + suspend fun getGundamList(): Result> + suspend fun getGundamDetail(id: String): Result> +} diff --git a/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailStateHolder.kt b/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailStateHolder.kt deleted file mode 100644 index 4943dfa..0000000 --- a/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailStateHolder.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.example.gundamdexapp.feature.detail - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import com.example.gundamdexapp.domain.model.GundamInfo -import com.example.gundamdexapp.feature.detail.uimodel.ArmamentUiModel -import com.example.gundamdexapp.feature.detail.uimodel.DimensionsUiModel -import com.example.gundamdexapp.feature.detail.uimodel.GundamdexDetailUiModel -import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor -import com.example.gundamdexapp.feature.detail.uimodel.TechnicalSpecificationsUiModel - -class GundamdexDetailStateHolder( - id: String, - gundamData: List, -) { - var uiState by mutableStateOf(GundamdexDetailUiState()) - - init { - uiState = ( - gundamData.firstOrNull { it.id == id }?.let { - uiState.copy( - id = id, - gundamdexDetailUiModel = GundamdexDetailUiModel( - modelNumber = it.modelNumber, - name = it.name, - series = it.series, - imageUrl = it.imageUrl, - era = it.era, - faction = it.faction, - pilot = it.pilot, - description = it.description, - dimensions = it.dimensions.let { data -> - DimensionsUiModel( - height = data.height, - weight = data.weight, - ) - }, - technicalSpecifications = it.technicalSpecifications.let { data -> - TechnicalSpecificationsUiModel( - generatorOutput = data.generatorOutput, - armorMaterial = data.armorMaterial, - totalTrust = data.totalTrust, - sensorRadius = data.sensorRadius, - crew = data.crew, - ) - }, - armaments = it.armaments.let { data -> - data.value.map { - ArmamentUiModel( - name = it.name, - details = it.details, - indicatorColor = IndicatorColor.dataToIndicatorColor(it.indicatorColor), - ) - } - }, - ), - ) - } ?: throw IllegalArgumentException("해당 카드의 id는 존재하지 않습니다.") - ) - } -} diff --git a/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailViewModel.kt b/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailViewModel.kt new file mode 100644 index 0000000..321989e --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/feature/detail/GundamdexDetailViewModel.kt @@ -0,0 +1,91 @@ +package com.example.gundamdexapp.feature.detail + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.CreationExtras +import com.example.gundamdexapp.GundamApplication +import com.example.gundamdexapp.domain.model.GundamInfo +import com.example.gundamdexapp.domain.repository.GundamRepository +import com.example.gundamdexapp.feature.detail.uimodel.ArmamentUiModel +import com.example.gundamdexapp.feature.detail.uimodel.DimensionsUiModel +import com.example.gundamdexapp.feature.detail.uimodel.GundamdexDetailUiModel +import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor +import com.example.gundamdexapp.feature.detail.uimodel.TechnicalSpecificationsUiModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class GundamdexDetailViewModel( + private val id: String, + private val repository: GundamRepository, +) : ViewModel() { + private val _uiState = MutableStateFlow(GundamdexDetailUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + viewModelScope.launch { + val gundamInfo = getGundamDetail(id) + setUiState(gundamInfo) + } + } + + suspend fun getGundamDetail(id: String): GundamInfo? = repository.getGundamDetail(id).getOrNull()?.firstOrNull() + + fun setUiState(gundamInfo: GundamInfo?) { + gundamInfo?.let { gundamInfo -> + _uiState.update { + it.copy( + id = id, + gundamdexDetailUiModel = GundamdexDetailUiModel( + modelNumber = gundamInfo.modelNumber, + name = gundamInfo.name, + series = gundamInfo.series, + imageUrl = gundamInfo.imageUrl, + era = gundamInfo.era, + faction = gundamInfo.faction, + pilot = gundamInfo.pilot, + description = gundamInfo.description, + dimensions = gundamInfo.dimensions.let { data -> + DimensionsUiModel( + height = data.height, + weight = data.weight, + ) + }, + technicalSpecifications = gundamInfo.technicalSpecifications.let { data -> + TechnicalSpecificationsUiModel( + generatorOutput = data.generatorOutput, + armorMaterial = data.armorMaterial, + totalTrust = data.totalTrust, + sensorRadius = data.sensorRadius, + crew = data.crew, + ) + }, + armaments = gundamInfo.armaments.let { data -> + data.value.map { value -> + ArmamentUiModel( + name = value.name, + details = value.details, + indicatorColor = IndicatorColor.dataToIndicatorColor(value.indicatorColor), + ) + } + }, + ), + ) + } + } ?: throw IllegalArgumentException("해당 id를 가진 건담에 대한 정보가 존재하지 않습니다.") + } + + class Factory(private val id: String) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class, extras: CreationExtras): T { + val application = checkNotNull(extras[APPLICATION_KEY]) as GundamApplication + val repository = application.appContainer.gundamRepository + + return GundamdexDetailViewModel(id = id, repository = repository) as T + } + } +} diff --git a/app/src/main/java/com/example/gundamdexapp/feature/detail/mapper/IndicatorColorMapper.kt b/app/src/main/java/com/example/gundamdexapp/feature/detail/mapper/IndicatorColorMapper.kt index 3680bc3..6ae7c1e 100644 --- a/app/src/main/java/com/example/gundamdexapp/feature/detail/mapper/IndicatorColorMapper.kt +++ b/app/src/main/java/com/example/gundamdexapp/feature/detail/mapper/IndicatorColorMapper.kt @@ -3,11 +3,11 @@ package com.example.gundamdexapp.feature.detail.mapper import androidx.compose.ui.graphics.Color import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor.BLUE -import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor.GRAY +import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor.GREY import com.example.gundamdexapp.feature.detail.uimodel.IndicatorColor.RED fun IndicatorColor.toColor(): Color = when (this) { RED -> Color.Red BLUE -> Color.Blue - GRAY -> Color.Gray + GREY -> Color.Gray } diff --git a/app/src/main/java/com/example/gundamdexapp/feature/detail/uimodel/IndicatorColor.kt b/app/src/main/java/com/example/gundamdexapp/feature/detail/uimodel/IndicatorColor.kt index c9e8748..f691990 100644 --- a/app/src/main/java/com/example/gundamdexapp/feature/detail/uimodel/IndicatorColor.kt +++ b/app/src/main/java/com/example/gundamdexapp/feature/detail/uimodel/IndicatorColor.kt @@ -3,14 +3,14 @@ package com.example.gundamdexapp.feature.detail.uimodel enum class IndicatorColor { RED, BLUE, - GRAY, + GREY, ; companion object { fun dataToIndicatorColor(value: String): IndicatorColor = when (value) { "red" -> RED "blue" -> BLUE - "gray" -> GRAY + "grey" -> GREY else -> throw IllegalArgumentException("잘못된 indicator color 입력 값을 전달받았습니다.") } } diff --git a/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeStatHolder.kt b/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeStatHolder.kt deleted file mode 100644 index 19ecc12..0000000 --- a/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeStatHolder.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.gundamdexapp.feature.home - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import com.example.gundamdexapp.domain.model.GundamInfo - -class GundamdexHomeStatHolder( - gundamData: List, -) { - var uiState by mutableStateOf(GundamdexHomeUiState()) - - init { - uiState = uiState.copy( - gundamInfoList = gundamData.map { - GundamdexHomeUiModel( - id = it.id, - modelNumber = it.modelNumber, - name = it.name, - series = it.series, - imageUrl = it.imageUrl, - ) - }, - ) - } -} diff --git a/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeViewModel.kt b/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeViewModel.kt new file mode 100644 index 0000000..50d94f5 --- /dev/null +++ b/app/src/main/java/com/example/gundamdexapp/feature/home/GundamdexHomeViewModel.kt @@ -0,0 +1,55 @@ +package com.example.gundamdexapp.feature.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.example.gundamdexapp.GundamApplication +import com.example.gundamdexapp.domain.repository.GundamRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class GundamdexHomeViewModel( + private val gundamRepository: GundamRepository, +) : ViewModel() { + private val _uiState = MutableStateFlow(GundamdexHomeUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + getGundamList() + } + + fun getGundamList() { + viewModelScope.launch { + _uiState.update { + it.copy( + gundamInfoList = gundamRepository.getGundamList() + .getOrThrow().map { gundamInfo -> + GundamdexHomeUiModel( + id = gundamInfo.id, + modelNumber = gundamInfo.modelNumber, + name = gundamInfo.name, + series = gundamInfo.series, + imageUrl = gundamInfo.imageUrl, + ) + }, + ) + } + } + } + + companion object { + val Factory = viewModelFactory { + initializer { + val application = this[APPLICATION_KEY] as GundamApplication + val repository = application.appContainer.gundamRepository + + GundamdexHomeViewModel(gundamRepository = repository) + } + } + } +} diff --git a/app/src/main/java/com/example/gundamdexapp/feature/navigation/GundamdexNavigation.kt b/app/src/main/java/com/example/gundamdexapp/feature/navigation/GundamdexNavigation.kt index 8ab25d4..4d4b810 100644 --- a/app/src/main/java/com/example/gundamdexapp/feature/navigation/GundamdexNavigation.kt +++ b/app/src/main/java/com/example/gundamdexapp/feature/navigation/GundamdexNavigation.kt @@ -2,21 +2,23 @@ package com.example.gundamdexapp.feature.navigation import androidx.compose.animation.SharedTransitionLayout import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute -import com.example.gundamdexapp.data.mock.GundamMockData import com.example.gundamdexapp.feature.detail.GundamdexDetail -import com.example.gundamdexapp.feature.detail.GundamdexDetailStateHolder +import com.example.gundamdexapp.feature.detail.GundamdexDetailViewModel import com.example.gundamdexapp.feature.home.GundamdexHome -import com.example.gundamdexapp.feature.home.GundamdexHomeStatHolder +import com.example.gundamdexapp.feature.home.GundamdexHomeViewModel @Composable -fun GundamdexNavigation() { +fun GundamdexNavigation( + gundamdexHomeViewModel: GundamdexHomeViewModel, +) { val navController = rememberNavController() - val gundamData = GundamMockData.mockGundams SharedTransitionLayout { NavHost( @@ -24,8 +26,7 @@ fun GundamdexNavigation() { startDestination = HomeRoute, ) { composable { - val gundamdexHomeStateHolder = remember { GundamdexHomeStatHolder(gundamData = gundamData) } - val uiState = gundamdexHomeStateHolder.uiState + val uiState by gundamdexHomeViewModel.uiState.collectAsState() GundamdexHome( gundamdexHomeUiState = uiState, @@ -41,13 +42,12 @@ fun GundamdexNavigation() { val routeData = backStackEntity.toRoute() val id = routeData.id - val gundamdexDetailStateHolder = remember { - GundamdexDetailStateHolder( + val viewModel: GundamdexDetailViewModel = viewModel( + factory = GundamdexDetailViewModel.Factory( id = id, - gundamData = gundamData, - ) - } - val uiState = remember { gundamdexDetailStateHolder.uiState } + ), + ) + val uiState by viewModel.uiState.collectAsState() GundamdexDetail( gundamdexDetailUiState = uiState, diff --git a/app/src/main/java/com/example/gundamdexapp/feature/utils/SharedTransitionKey.kt b/app/src/main/java/com/example/gundamdexapp/feature/utils/SharedTransitionKey.kt index 3e8a1ae..4163467 100644 --- a/app/src/main/java/com/example/gundamdexapp/feature/utils/SharedTransitionKey.kt +++ b/app/src/main/java/com/example/gundamdexapp/feature/utils/SharedTransitionKey.kt @@ -2,9 +2,10 @@ package com.example.gundamdexapp.feature.utils data class SharedTransitionKey( val id: String, - val type: SharedType + val type: SharedType, ) enum class SharedType { - COMPOSABLE, IMAGE + COMPOSABLE, + IMAGE, } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 442c728..0b3023c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] agp = "9.2.1" +bom = "3.6.0" coilCompose = "3.5.0" converterKotlinxSerialization = "2.11.0" coreKtx = "1.16.0" @@ -9,6 +10,7 @@ espressoCore = "3.7.0" kotlinxCoroutinesAndroid = "1.11.0" kotlinxCoroutinesCore = "1.11.0" kotlinxSerializationJson = "1.11.0" +ktorClientAndroid = "3.5.0" lifecycleRuntimeKtx = "2.10.0" activityCompose = "1.13.0" kotlin = "2.4.0" @@ -19,11 +21,13 @@ retrofit = "2.11.0" ktlint = "14.2.0" [libraries] +androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityCompose" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } +bom = { module = "io.github.jan-tennert.supabase:bom", version.ref = "bom" } coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilCompose" } converter-kotlinx-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "converterKotlinxSerialization" } @@ -43,7 +47,9 @@ androidx-compose-material3 = { group = "androidx.compose.material3", name = "mat kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } +ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktorClientAndroid" } logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" } +postgrest-kt = { module = "io.github.jan-tennert.supabase:postgrest-kt" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } [plugins]