diff --git a/feature/explore/src/main/java/kr/co/di/ExploreModule.kt b/feature/explore/src/main/java/kr/co/di/ExploreModule.kt new file mode 100644 index 0000000..6327709 --- /dev/null +++ b/feature/explore/src/main/java/kr/co/di/ExploreModule.kt @@ -0,0 +1,14 @@ +package kr.co.di + +import kr.co.explore.ExploreViewModel +import org.koin.core.module.dsl.viewModel +import org.koin.dsl.module + +val exploreModule = + module { + viewModel { + ExploreViewModel( + get() + ) + } + } diff --git a/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt b/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt index fc7efc7..36e6575 100644 --- a/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt +++ b/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt @@ -13,25 +13,27 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import kr.co.model.ExploreSideEffect +import kr.co.model.ExploreUiIntent +import kr.co.model.ExploreUiState import kr.co.model.FileInfo import kr.co.seedocs.feature.explore.R import kr.co.ui.theme.SeeDocsTheme import kr.co.ui.theme.Theme +import kr.co.ui.util.LaunchIntentHandler +import kr.co.ui.util.LaunchSideEffect import kr.co.ui.widget.FileBox import kr.co.util.DEFAULT_STORAGE -import kr.co.util.readPDFOrDirectory import kr.co.widget.FolderBox +import org.koin.androidx.compose.koinViewModel @Composable internal fun ExploreRoute( @@ -39,30 +41,31 @@ internal fun ExploreRoute( padding: PaddingValues, navigateToFolder: (String) -> Unit = {}, navigateToPdf: (String) -> Unit = {}, + viewModel: ExploreViewModel = koinViewModel(), ) { - var files by remember { mutableStateOf>(emptyList()) } + val state by viewModel.uiState.collectAsStateWithLifecycle() - LaunchedEffect(Unit) { - files = readPDFOrDirectory(path) + LaunchIntentHandler(ExploreUiIntent.Init(path), viewModel) + + LaunchSideEffect(viewModel) { + when (it) { + is ExploreSideEffect.NavigateToPdf -> navigateToPdf(it.path) + is ExploreSideEffect.NavigateToFolder -> navigateToFolder(it.path) + } } ExploreScreen( - path = path.replace(DEFAULT_STORAGE, - stringResource(R.string.feature_explore_local_storage)), - files = files, + state = state, padding = padding, - onFolderClick = { folderPath -> navigateToFolder(folderPath) }, - onFileClick = navigateToPdf + handleIntent = viewModel::handleIntent, ) } @Composable private fun ExploreScreen( - path: String, - files: List = emptyList(), + state: ExploreUiState = ExploreUiState.INIT, padding: PaddingValues, - onFolderClick: (String) -> Unit = {}, - onFileClick: (String) -> Unit = {} + handleIntent: (ExploreUiIntent) -> Unit = {}, ) { Box( modifier = Modifier @@ -93,13 +96,28 @@ private fun ExploreScreen( ) Text( text = buildAnnotatedString { - append(">${path.split("/").dropLast(1).joinToString(separator = "/")}") - append("/") - withStyle( - Theme.typography.caption1r.copy(color = Theme.colors.highlight) - .toSpanStyle() - ) { - append(path.split("/").last()) + val relativePath = state.path.replace( + DEFAULT_STORAGE, + stringResource(R.string.feature_explore_local_storage) + ) + val pathSegments = relativePath.split("/") + + if (pathSegments.size <= 1) { + withStyle( + Theme.typography.caption1r.copy(color = Theme.colors.highlight).toSpanStyle() + ) { + append("> ${pathSegments.first()}") + } + } else { + append("> ") + pathSegments.dropLast(1).forEach { segment -> + append("$segment/") + } + withStyle( + Theme.typography.caption1r.copy(color = Theme.colors.highlight).toSpanStyle() + ) { + append(pathSegments.last()) + } } }, style = Theme.typography.caption1r, @@ -108,21 +126,21 @@ private fun ExploreScreen( } } - items(files.filter { it.isDirectory }) { folder -> + items(state.files.filter { it.isDirectory }) { folder -> FolderBox( name = folder.name, - onClick = { onFolderClick(folder.path) } + onClick = { handleIntent(ExploreUiIntent.ClickFolder(folder)) } ) } items( - items = files.filter { !it.isDirectory }, + items = state.files.filter { !it.isDirectory }, span = { GridItemSpan(maxLineSpan) } ) { file -> FileBox( name = file.name, dateTime = file.createdAt, - onFileClick = { onFileClick(file.path) } + onFileClick = { handleIntent(ExploreUiIntent.ClickFile(file)) } ) } } @@ -134,7 +152,6 @@ private fun ExploreScreen( private fun Preview() { SeeDocsTheme { ExploreScreen( - path = DEFAULT_STORAGE, padding = PaddingValues(), ) } diff --git a/feature/explore/src/main/java/kr/co/explore/ExploreViewModel.kt b/feature/explore/src/main/java/kr/co/explore/ExploreViewModel.kt new file mode 100644 index 0000000..8a47692 --- /dev/null +++ b/feature/explore/src/main/java/kr/co/explore/ExploreViewModel.kt @@ -0,0 +1,45 @@ +package kr.co.explore + +import kr.co.data.repository.RecentRepository +import kr.co.model.ExploreSideEffect +import kr.co.model.ExploreUiIntent +import kr.co.model.ExploreUiState +import kr.co.model.FileInfo +import kr.co.ui.base.BaseMviViewModel +import kr.co.util.readPDFOrDirectory + +internal class ExploreViewModel( + private val recentRepository: RecentRepository, +) : + BaseMviViewModel(ExploreUiState.INIT) { + + override fun handleIntent(intent: ExploreUiIntent) { + when (intent) { + is ExploreUiIntent.Init -> init(intent.path) + is ExploreUiIntent.ClickFile -> onClickFile(intent.file) + is ExploreUiIntent.ClickFolder -> onClickFolder(intent.folder) + } + } + + private fun init(path: String) = launch { + path.debugLog("path") + readPDFOrDirectory(path).let { + reduce { + copy( + path = path, + files = it + ) + } + } + } + + private fun onClickFile(file: FileInfo) = launch { + recentRepository.insert(file) + }.invokeOnCompletion { + postSideEffect(ExploreSideEffect.NavigateToPdf(file.path)) + } + + private fun onClickFolder(folder: FileInfo) = + postSideEffect(ExploreSideEffect.NavigateToFolder(folder.path)) + +} \ No newline at end of file diff --git a/feature/explore/src/main/java/kr/co/model/ExploreSideEffect.kt b/feature/explore/src/main/java/kr/co/model/ExploreSideEffect.kt new file mode 100644 index 0000000..24364f8 --- /dev/null +++ b/feature/explore/src/main/java/kr/co/model/ExploreSideEffect.kt @@ -0,0 +1,8 @@ +package kr.co.model + +import kr.co.ui.base.UiSideEffect + +internal sealed interface ExploreSideEffect: UiSideEffect { + data class NavigateToFolder(val path: String) : ExploreSideEffect + data class NavigateToPdf(val path: String) : ExploreSideEffect +} \ No newline at end of file diff --git a/feature/explore/src/main/java/kr/co/model/ExploreUiIntent.kt b/feature/explore/src/main/java/kr/co/model/ExploreUiIntent.kt new file mode 100644 index 0000000..1258d29 --- /dev/null +++ b/feature/explore/src/main/java/kr/co/model/ExploreUiIntent.kt @@ -0,0 +1,9 @@ +package kr.co.model + +import kr.co.ui.base.UiIntent + +internal sealed interface ExploreUiIntent: UiIntent { + data class Init(val path: String): ExploreUiIntent + data class ClickFile(val file: FileInfo): ExploreUiIntent + data class ClickFolder(val folder: FileInfo): ExploreUiIntent +} \ No newline at end of file diff --git a/feature/explore/src/main/java/kr/co/model/ExploreUiState.kt b/feature/explore/src/main/java/kr/co/model/ExploreUiState.kt new file mode 100644 index 0000000..2659d6a --- /dev/null +++ b/feature/explore/src/main/java/kr/co/model/ExploreUiState.kt @@ -0,0 +1,15 @@ +package kr.co.model + +import kr.co.ui.base.UiState + +internal data class ExploreUiState( + val path: String, + val files: List, +): UiState { + companion object { + val INIT = ExploreUiState( + path = "", + files = emptyList(), + ) + } +} diff --git a/feature/main/src/main/java/kr/co/main/di/MainModule.kt b/feature/main/src/main/java/kr/co/main/di/MainModule.kt index 2f21431..5e7161a 100644 --- a/feature/main/src/main/java/kr/co/main/di/MainModule.kt +++ b/feature/main/src/main/java/kr/co/main/di/MainModule.kt @@ -1,6 +1,7 @@ package kr.co.main.di import kr.co.di.bookmarkModule +import kr.co.di.exploreModule import kr.co.di.recentModule import kr.co.di.pdfModule import org.koin.dsl.module @@ -8,6 +9,7 @@ import org.koin.dsl.module val mainModule = module { includes( + exploreModule, recentModule, bookmarkModule, pdfModule,