Skip to content
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.ohnalmwo.data.di

import com.ohnalmwo.data.repository.LocationRepositoryImpl
import com.ohnalmwo.data.repository.MainRepositoryImpl
import com.ohnalmwo.data.repository.SettingRepositoryImpl
import com.ohnalmwo.data.repository.WeatherRepositoryImpl
import com.ohnalmwo.domain.repository.LocationRepository
import com.ohnalmwo.domain.repository.MainRepository
import com.ohnalmwo.domain.repository.SettingRepository
import com.ohnalmwo.domain.repository.WeatherRepository
import dagger.Binds
Expand Down Expand Up @@ -32,4 +34,10 @@ abstract class RepositoryModule {
abstract fun bindSettingRepository(
settingRepositoryImpl: SettingRepositoryImpl
): SettingRepository

@Binds
@Singleton
abstract fun bindMainRepository(
mainRepositoryImpl: MainRepositoryImpl
): MainRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ohnalmwo.data.repository

import com.ohnalmwo.datastore.datasource.main.MainDataSource
import com.ohnalmwo.domain.repository.MainRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class MainRepositoryImpl @Inject constructor(
private val localMainDataSource: MainDataSource
) : MainRepository {
override fun getTutorialDialogState(): Flow<Boolean> =
localMainDataSource.getTutorialDialogState()

override suspend fun setTutorialDialogState(openDialog: Boolean) {
localMainDataSource.setTutorialDialogState(openDialog = openDialog)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ohnalmwo.datastore.datasource.main

import kotlinx.coroutines.flow.Flow

interface MainDataSource {
fun getTutorialDialogState(): Flow<Boolean>

suspend fun setTutorialDialogState(openDialog: Boolean)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.ohnalmwo.datastore.datasource.main

import androidx.datastore.core.DataStore
import com.ohnalmwo.datastore.Tutorial
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class MainDataSourceImpl @Inject constructor(
private val tutorial: DataStore<Tutorial>
) : MainDataSource {
override fun getTutorialDialogState(): Flow<Boolean> =
tutorial.data.map { it.openDialog }

override suspend fun setTutorialDialogState(openDialog: Boolean) {
tutorial.updateData {
it.toBuilder()
.setOpenDialog(openDialog)
.build()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import com.ohnalmwo.datastore.Locations
import com.ohnalmwo.datastore.SettingInfo
import com.ohnalmwo.datastore.Tutorial
import com.ohnalmwo.datastore.serializer.LocationsSerializer
import com.ohnalmwo.datastore.serializer.MainSerializer
import com.ohnalmwo.datastore.serializer.SettingSerializer
import dagger.Module
import dagger.Provides
Expand All @@ -30,6 +32,18 @@ object DataStoreModule {
context.dataStoreFile("locations.pb")
}

@Provides
@Singleton
fun provideMainDataStore(
@ApplicationContext context: Context,
mainSerializer: MainSerializer
): DataStore<Tutorial> =
DataStoreFactory.create(
serializer = mainSerializer,
) {
context.dataStoreFile("tutorial.pb")
}

@Provides
@Singleton
fun provideSettingDataStore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.ohnalmwo.datastore.di

import com.ohnalmwo.datastore.datasource.location.LocationsDataSource
import com.ohnalmwo.datastore.datasource.location.LocationsDataSourceImpl
import com.ohnalmwo.datastore.datasource.main.MainDataSource
import com.ohnalmwo.datastore.datasource.main.MainDataSourceImpl
import com.ohnalmwo.datastore.datasource.setting.SettingDataSource
import com.ohnalmwo.datastore.datasource.setting.SettingDataSourceImpl
import dagger.Binds
Expand All @@ -19,6 +21,12 @@ abstract class LocalDataSourceModule {
locationsDataSourceImpl: LocationsDataSourceImpl
): LocationsDataSource

@Binds
@Singleton
abstract fun bindMainDataSource(
mainDataSourceImpl: MainDataSourceImpl
): MainDataSource

@Binds
@Singleton
abstract fun bindSettingDataSource(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ohnalmwo.datastore.serializer

import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import com.google.protobuf.InvalidProtocolBufferException
import com.ohnalmwo.datastore.Tutorial
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Inject

class MainSerializer @Inject constructor() : Serializer<Tutorial> {
override val defaultValue: Tutorial = Tutorial.newBuilder()
.setOpenDialog(true)
.build()

override suspend fun readFrom(input: InputStream): Tutorial =
try {
Tutorial.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto", e)
}

override suspend fun writeTo(t: Tutorial, output: OutputStream) {
t.writeTo(output)
}
}
8 changes: 8 additions & 0 deletions core/datastore/src/main/proto/tutorial.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";

option java_package = "com.ohnalmwo.datastore";
option java_multiple_files = true;

message Tutorial {
bool open_dialog = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ohnalmwo.domain.repository

import kotlinx.coroutines.flow.Flow

interface MainRepository {
fun getTutorialDialogState(): Flow<Boolean>

suspend fun setTutorialDialogState(openDialog: Boolean)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ohnalmwo.domain.usecase.main

import com.ohnalmwo.domain.repository.MainRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class GetTutorialDialogStateUseCase @Inject constructor(
private val mainRepository: MainRepository
) {
operator fun invoke(): Flow<Boolean> =
mainRepository.getTutorialDialogState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ohnalmwo.domain.usecase.main

import com.ohnalmwo.domain.repository.MainRepository
import javax.inject.Inject

class SetTutorialDialogStateUseCase @Inject constructor(
private val mainRepository: MainRepository
) {
suspend operator fun invoke(openDialog: Boolean) {
mainRepository.setTutorialDialogState(openDialog = openDialog)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,34 +57,4 @@ fun LoadingLocationCard(
}
}
}
}

@Composable
private fun LoadingWeatherForecastCard(modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.height(160.dp)
.clip(RoundedCornerShape(16.dp))
.background(color = colors.WHITE.copy(alpha = .2f))
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "시간 별 일기예보",
style = typography.textSmall,
fontWeight = FontWeight.Bold,
color = colors.WHITE.copy(.75f)
)
ShimmerBox(
Modifier
.fillMaxWidth(.8f)
.height(16.dp)
)
ShimmerBox(
Modifier
.fillMaxWidth(.5f)
.height(16.dp)
)
}
}
14 changes: 8 additions & 6 deletions feature/main/src/main/java/com/ohnalmwo/main/MainScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fun MainRoute(
LaunchedEffect(Unit) {
viewModel.getWeatherSignificant(x = 126.85250, y = 35.15944)
viewModel.getSavedLocations()
viewModel.getTutorialDialogState()
}

LaunchedEffect(effect) {
Expand All @@ -43,6 +44,7 @@ fun MainRoute(
MainScreen(
hazeState = hazeState,
state = state,
onDismissClick = viewModel::setTutorialDialogState,
navigateToLocation = { viewModel.sendEffect(MainEffect.NavigateToLocation) }
)
}
Expand All @@ -51,6 +53,7 @@ fun MainRoute(
fun MainScreen(
hazeState: HazeState,
state: MainState,
onDismissClick: (Boolean) -> Unit,
navigateToLocation: () -> Unit
) {
val context = LocalContext.current
Expand All @@ -60,7 +63,6 @@ fun MainScreen(
.readBytes()
.decodeToString()
}
var openDialog by remember { mutableStateOf(true) }
val weathers = state.significant.weathers

if (state.isLoading) {
Expand All @@ -75,12 +77,12 @@ fun MainScreen(
)
}

if (openDialog) {
if (state.openDialog) {
TutorialDialog(
openDialog = openDialog,
onStateChange = { openDialog = it },
onDismissClick = { openDialog = false },
onCheckClick = { openDialog = false }
openDialog = state.openDialog,
onStateChange = onDismissClick,
onDismissClick = { onDismissClick(false) },
onCheckClick = { onDismissClick(false) }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class MainScreenReducer : Reducer<MainScreenReducer.MainState, MainScreenReducer
sealed class MainEvent : Reducer.ViewEvent {
data class GetWeatherSignificant(val isLoading: Boolean, val significant: Weather) : MainEvent()
data class GetSavedLocations(val isLoading: Boolean, val localLocations: List<LocationInfo>) : MainEvent()
data class GetTutorialDialogState(val isLoading: Boolean, val openDialog: Boolean) : MainEvent()
data class SetTutorialDialogState(val openDialog: Boolean) : MainEvent()
}

@Immutable
Expand All @@ -22,13 +24,15 @@ class MainScreenReducer : Reducer<MainScreenReducer.MainState, MainScreenReducer
data class MainState(
val isLoading: Boolean,
val significant: Weather,
val localLocations: List<LocationInfo>
val localLocations: List<LocationInfo>,
val openDialog: Boolean
) : Reducer.ViewState {
companion object {
fun initial() = MainState(
isLoading = true,
significant = Weather.default(),
localLocations = emptyList()
localLocations = emptyList(),
openDialog = false
)
}
}
Expand All @@ -50,5 +54,18 @@ class MainScreenReducer : Reducer<MainScreenReducer.MainState, MainScreenReducer
localLocations = event.localLocations
) to null
}

is MainEvent.GetTutorialDialogState -> {
previousState.copy(
isLoading = event.isLoading,
openDialog = event.openDialog
) to null
}

is MainEvent.SetTutorialDialogState -> {
previousState.copy(
openDialog = event.openDialog
) to null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.ohnalmwo.common.base.BaseViewModel
import com.ohnalmwo.common.result.Result
import com.ohnalmwo.common.result.asResult
import com.ohnalmwo.domain.usecase.location.GetSavedLocationsUseCase
import com.ohnalmwo.domain.usecase.main.GetTutorialDialogStateUseCase
import com.ohnalmwo.domain.usecase.main.SetTutorialDialogStateUseCase
import com.ohnalmwo.domain.usecase.weather.GetWeatherSignificantUseCase
import com.ohnalmwo.main.viewmodel.MainScreenReducer.*
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -14,7 +16,9 @@ import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
private val getWeatherSignificantUseCase: GetWeatherSignificantUseCase,
private val getSavedLocationsUseCase: GetSavedLocationsUseCase
private val getSavedLocationsUseCase: GetSavedLocationsUseCase,
private val getTutorialDialogStateUseCase: GetTutorialDialogStateUseCase,
private val setTutorialDialogStateUseCase: SetTutorialDialogStateUseCase
) : BaseViewModel<MainState, MainEvent, MainEffect>(
initialState = MainState.initial(),
reducer = MainScreenReducer()
Expand All @@ -37,9 +41,26 @@ class MainViewModel @Inject constructor(
.collect { result ->
when (result) {
is Result.Loading -> sendEvent(event = MainEvent.GetSavedLocations(isLoading = true, localLocations = currentState.localLocations))
is Result.Success -> { sendEvent(event = MainEvent.GetSavedLocations(isLoading = false, localLocations = result.data)) }
is Result.Error -> { sendEvent(event = MainEvent.GetSavedLocations(isLoading = false, localLocations = currentState.localLocations)) }
is Result.Success -> sendEvent(event = MainEvent.GetSavedLocations(isLoading = false, localLocations = result.data))
is Result.Error -> sendEvent(event = MainEvent.GetSavedLocations(isLoading = false, localLocations = currentState.localLocations))
}
}
}

fun getTutorialDialogState() = viewModelScope.launch {
getTutorialDialogStateUseCase()
.asResult()
.collect { result ->
when (result) {
is Result.Loading -> sendEvent(event = MainEvent.GetTutorialDialogState(isLoading = true, openDialog = currentState.openDialog))
is Result.Success -> sendEvent(event = MainEvent.GetTutorialDialogState(isLoading = false, openDialog = result.data))
is Result.Error -> sendEvent(event = MainEvent.GetTutorialDialogState(isLoading = false, openDialog = currentState.openDialog))
}
}
}

fun setTutorialDialogState(openDialog: Boolean) = viewModelScope.launch {
setTutorialDialogStateUseCase(openDialog = openDialog)
sendEvent(MainEvent.SetTutorialDialogState(openDialog = openDialog))
}
}