Skip to content

Commit

Permalink
common file repo
Browse files Browse the repository at this point in the history
  • Loading branch information
softartdev committed Jul 3, 2023
1 parent b78b24d commit 68d093d
Show file tree
Hide file tree
Showing 17 changed files with 334 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ sealed class Configuration : Parcelable {

@Parcelize
object Settings : Configuration()

@Parcelize
object Files : Configuration()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class NoteDelightRootPreview : NoteDelightRoot {
mainViewModel = getViewModel(),
onItemClicked = {},
onSettingsClick = {},
navSignIn = {}
navSignIn = {},
onFilesClick = {}
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class RootComponent(
is Configuration.Main -> mainList()
is Configuration.Details -> noteDetail(configuration, context)
is Configuration.Settings -> settings()
is Configuration.Files -> fileList()
}

private fun splash(): ContentChild = {
Expand All @@ -51,6 +52,7 @@ class RootComponent(
onItemClicked = { id -> navigation.push(Configuration.Details(itemId = id)) },
onSettingsClick = { navigation.push(Configuration.Settings) },
navSignIn = { navigation.replaceCurrent(Configuration.SignIn) },
onFilesClick = { navigation.push(Configuration.Files) },
)
}

Expand All @@ -75,6 +77,13 @@ class RootComponent(
)
}

private fun fileList(): ContentChild = {
FileListScreen(
onBackClick = navigation::pop,
filesViewModel = getViewModel(),
)
}

class BackWrapper(var handler: (() -> Unit)? = null): () -> Boolean {
override fun invoke(): Boolean {
handler?.invoke() ?: return false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.softartdev.notedelight.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.unit.dp
import com.softartdev.notedelight.shared.presentation.files.FilesResult
import com.softartdev.notedelight.shared.presentation.files.FilesViewModel

@Composable
fun FileListScreen(
onBackClick: () -> Unit = {},
filesViewModel: FilesViewModel
) {
val fileListState: State<FilesResult> = filesViewModel.resultStateFlow.collectAsState()
DisposableEffect(filesViewModel) {
filesViewModel.updateFiles()
onDispose(filesViewModel::onCleared)
}
FileListScreen(fileListState, onBackClick, filesViewModel::onItemClicked)
}

@Composable
fun FileListScreen(
fileListState: State<FilesResult>,
onBackClick: () -> Unit = {},
onItemClicked: (text: String) -> Unit = {}
) = Scaffold(
topBar = {
TopAppBar(
title = { Text("File list") },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = Icons.Default.ArrowBack.name
)
}
},
)
},
content = {
when (val result = fileListState.value) {
is FilesResult.Loading -> Loader()
is FilesResult.Success -> {
val fileList: List<String> = result.result
if (fileList.isEmpty()) Empty() else FileList(fileList, onItemClicked)
}

is FilesResult.Error -> Error(err = result.error ?: "Unknown error")
}
}
)

@Composable
fun FileList(fileList: List<String>, onItemClicked: (text: String) -> Unit) {
val listState = rememberLazyListState()

LazyColumn(state = listState) {
items(items = fileList) {
FileItem(
fileName = it,
onItemClicked = onItemClicked,
)
Divider()
}
}
LaunchedEffect(key1 = fileList.size, key2 = listState) {
listState.animateScrollToItem(0)
}
}

@Composable
fun FileItem(fileName: String, onItemClicked: (fileName: String) -> Unit) = Column(
modifier = Modifier
.clickable { onItemClicked(fileName) }
.fillMaxWidth()
.padding(4.dp)
.clearAndSetSemantics { contentDescription = fileName }
) {
Text(
text = fileName,
style = MaterialTheme.typography.h6,
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
package com.softartdev.notedelight.ui

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.material.*
import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
Expand All @@ -21,14 +33,15 @@ fun MainScreen(
mainViewModel: MainViewModel,
onItemClicked: (id: Long) -> Unit,
onSettingsClick: () -> Unit,
navSignIn: () -> Unit
navSignIn: () -> Unit,
onFilesClick: () -> Unit
) {
val noteListState: State<NoteListResult> = mainViewModel.resultStateFlow.collectAsState()
DisposableEffect(mainViewModel) {
mainViewModel.updateNotes()
onDispose(mainViewModel::onCleared)
}
MainScreen(noteListState, onItemClicked, onSettingsClick, navSignIn)
MainScreen(noteListState, onItemClicked, onSettingsClick, navSignIn, onFilesClick)
}

@Composable
Expand All @@ -37,6 +50,7 @@ fun MainScreen(
onItemClicked: (id: Long) -> Unit = {},
onSettingsClick: () -> Unit = {},
navSignIn: () -> Unit = {},
onFilesClick: () -> Unit = {}
) = Scaffold(
topBar = {
TopAppBar(
Expand All @@ -45,6 +59,9 @@ fun MainScreen(
IconButton(onClick = onSettingsClick) {
Icon(Icons.Default.Settings, contentDescription = stringResource(MR.strings.settings))
}
IconButton(onClick = onFilesClick) {
Icon(Icons.Default.Folder, contentDescription = "Open files")
}
})
}, content = {
when (val noteListResult = noteListState.value) {
Expand Down
1 change: 1 addition & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ kotlin {
api(libs.mokoResources)
implementation(libs.koin.core)
api(libs.material.theme.prefs)
implementation("com.squareup.okio:okio:3.3.0")
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package com.softartdev.notedelight.shared.di

import com.softartdev.notedelight.shared.database.AndroidDbRepo
import com.softartdev.notedelight.shared.database.DatabaseRepo
import com.softartdev.notedelight.shared.files.AndroidFileRepo
import com.softartdev.notedelight.shared.files.FileRepo
import org.koin.core.module.Module
import org.koin.dsl.module

actual val repoModule: Module = module {
single<DatabaseRepo> { AndroidDbRepo(get()) }
single<FileRepo> { AndroidFileRepo(context = get()) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.softartdev.notedelight.shared.files

import android.content.Context
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath

class AndroidFileRepo(context: Context) : FileRepo(
fileSystem = FileSystem.SYSTEM,
zeroPath = zeroPath(context)
) {

companion object {

private fun zeroPath(context: Context): Path {
val filesDir = context.filesDir
return filesDir.path.toPath()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ abstract class BaseViewModel<T> : KmmViewModel() {
}.start()
}

fun launchAction(
useIdling: Boolean = true,
blockAction: () -> Unit
) {
viewModelScope.launch(Dispatchers.IO) {
try {
if (useIdling) IdlingResource.increment()
blockAction.invoke()
} catch (e: Throwable) {
Napier.e("", e)
onResult(errorResult(e))
} finally {
if (useIdling) IdlingResource.decrement()
}
}.start()
}

private suspend inline fun onResult(result: T) = withContext(Dispatchers.Main) {
_resultStateFlow.value = result
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.softartdev.notedelight.shared.di
import com.softartdev.notedelight.shared.data.CryptUseCase
import com.softartdev.notedelight.shared.data.NoteUseCase
import com.softartdev.notedelight.shared.database.DatabaseRepo
import com.softartdev.notedelight.shared.presentation.files.FilesViewModel
import com.softartdev.notedelight.shared.presentation.main.MainViewModel
import com.softartdev.notedelight.shared.presentation.note.NoteViewModel
import com.softartdev.notedelight.shared.presentation.settings.SettingsViewModel
Expand Down Expand Up @@ -40,4 +41,5 @@ val viewModelModule: Module = module {
viewModelFactory { EnterViewModel(get()) }
viewModelFactory { ConfirmViewModel(get()) }
viewModelFactory { ChangeViewModel(get()) }
viewModelFactory { FilesViewModel(get()) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.softartdev.notedelight.shared.files

import io.github.aakira.napier.Napier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import okio.FileMetadata
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath
import okio.buffer
import okio.use

abstract class FileRepo(private val fileSystem: FileSystem, zeroPath: Path) {
private val upFolder = "🔙.."
private val fileContent = "📖"
private lateinit var currentFileDir: Path
private lateinit var currentFiles: List<Path>
private lateinit var currentFileNames: List<String>
private val _fileListFlow: MutableStateFlow<List<String>> = MutableStateFlow(
value = listOf("🔁loading...")
)
val fileListFlow: Flow<List<String>> = _fileListFlow

init {
goTo(file = zeroPath)
}

fun onItemClicked(fileName: String) = when (fileName) {
upFolder -> goTo(file = requireNotNull(currentFileDir.parent))
fileContent -> {
_fileListFlow.value = _fileListFlow.value + fileContent // FIXME
}
else -> {
val index = currentFileNames.indexOf(fileName)
if (index != -1) {
val file = currentFiles[index]
goTo(file)
} else Napier.e("file not found: $fileName")
}
}

private fun goTo(file: Path) {
Napier.d("📂go to: $file")
val metadata: FileMetadata = fileSystem.metadataOrNull(file) ?: return
if (metadata.isDirectory) {
currentFileDir = file
currentFiles = fileSystem.list(dir = file)
currentFileNames = currentFiles.map { curFile: Path ->
val curMetadata = fileSystem.metadataOrNull(curFile) ?: return@map ""
val icon = if (curMetadata.isDirectory) "📁" else "📄"
return@map "$icon ${curFile.name}"
}
} else if (metadata.isRegularFile) {
currentFileDir = file
currentFiles = emptyList()
currentFileNames = listOf(fileContent, readFile(file))
} else {
Napier.e("unknown file: $file")
_fileListFlow.value = _fileListFlow.value + "$metadata" // FIXME
}
val absolutePath = if (file.isAbsolute) file else fileSystem.canonicalize(file)
_fileListFlow.value = listOf("📂${absolutePath.toString()}", upFolder) + currentFileNames
}

private fun readFile(file: Path): String {
val stringBuilder = StringBuilder()
fileSystem.source(file).use { fileSource ->
fileSource.buffer().use { bufferedFileSource ->
while (true) {
val line = bufferedFileSource.readUtf8Line() ?: break
stringBuilder.append(line)
}
}
}
return stringBuilder.toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.softartdev.notedelight.shared.presentation.files


sealed class FilesResult {
object Loading : FilesResult()
data class Success(val result: List<String>) : FilesResult()
data class Error(val error: String? = null) : FilesResult()
}
Loading

0 comments on commit 68d093d

Please sign in to comment.