Cleanup and optimize RepositoryList

This commit is contained in:
Iamlooker 2022-06-29 19:31:53 +05:30
parent 555ebac697
commit c7ba399b53
No known key found for this signature in database
GPG Key ID: 16F53B972BAECA48
3 changed files with 92 additions and 81 deletions

View File

@ -0,0 +1,40 @@
package com.looker.droidify.ui.compose.pages.settings.repository
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.looker.droidify.ui.compose.RepositoriesRecycler
import com.looker.droidify.ui.viewmodels.RepositoriesViewModelX
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RepositoryPage(viewModel: RepositoriesViewModelX) {
val repos by viewModel.repositories.collectAsState()
Scaffold(
modifier = Modifier.fillMaxSize(),
floatingActionButton = {
ExtendedFloatingActionButton(onClick = { viewModel.addRepository() }) {
Icon(imageVector = Icons.Rounded.Add, contentDescription = "Add Repository")
Text(text = "Add Repository")
}
}
) {
val sortedRepoList = remember { repos.sortedBy { !it.enabled } }
RepositoriesRecycler(
repositoriesList = sortedRepoList,
onClick = { viewModel.toggleRepository(it, it.enabled) },
onLongClick = { viewModel.showRepositorySheet(it.id) }
)
}
}

View File

@ -5,64 +5,47 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.looker.droidify.R import androidx.lifecycle.lifecycleScope
import com.looker.droidify.content.Preferences import com.looker.droidify.content.Preferences
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.ui.activities.PrefsActivityX import com.looker.droidify.ui.activities.PrefsActivityX
import com.looker.droidify.ui.compose.RepositoriesRecycler import com.looker.droidify.ui.compose.pages.settings.repository.RepositoryPage
import com.looker.droidify.ui.compose.theme.AppTheme import com.looker.droidify.ui.compose.theme.AppTheme
import com.looker.droidify.ui.viewmodels.RepositoriesViewModelX import com.looker.droidify.ui.viewmodels.RepositoriesViewModelX
import com.looker.droidify.utility.isDarkTheme import com.looker.droidify.utility.isDarkTheme
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class PrefsRepositoriesFragment : BaseNavFragment() { class PrefsRepositoriesFragment : BaseNavFragment() {
val viewModel: RepositoriesViewModelX by viewModels { val viewModel: RepositoriesViewModelX by viewModels {
RepositoriesViewModelX.Factory(prefsActivityX.db) RepositoriesViewModelX.Factory(prefsActivityX.db.repositoryDao)
} }
private val prefsActivityX: PrefsActivityX private val prefsActivityX: PrefsActivityX
get() = requireActivity() as PrefsActivityX get() = requireActivity() as PrefsActivityX
private val syncConnection = Connection(SyncService::class.java)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View { ): View {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lifecycleScope.launchWhenCreated {
viewModel.showSheet.collectLatest {
it?.let {
RepositorySheetX(it).showNow(childFragmentManager, "Repository $it")
}
}
}
return ComposeView(requireContext()).apply { return ComposeView(requireContext()).apply {
setContent { ReposPage() } setContent { ReposPage() }
} }
} }
override fun setupLayout() { override fun setupLayout() {
syncConnection.bind(requireContext()) viewModel.bindConnection(requireContext())
viewModel.toLaunch.observe(viewLifecycleOwner) { viewModel.toLaunch.observe(viewLifecycleOwner) {
if (it?.first == true) { if (it?.first == true) {
EditRepositorySheetX(it.second) EditRepositorySheetX(it.second)
@ -74,14 +57,11 @@ class PrefsRepositoriesFragment : BaseNavFragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
syncConnection.unbind(requireContext()) viewModel.syncConnection.unbind(requireContext())
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ReposPage() { fun ReposPage() {
val repos by viewModel.repositories.observeAsState(null)
AppTheme( AppTheme(
darkTheme = when (Preferences[Preferences.Key.Theme]) { darkTheme = when (Preferences[Preferences.Key.Theme]) {
is Preferences.Theme.System -> isSystemInDarkTheme() is Preferences.Theme.System -> isSystemInDarkTheme()
@ -89,46 +69,7 @@ class PrefsRepositoriesFragment : BaseNavFragment() {
else -> isDarkTheme else -> isDarkTheme
} }
) { ) {
Scaffold { padding -> RepositoryPage(viewModel = viewModel)
Column(
modifier = Modifier.padding(padding)
) {
OutlinedButton(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
contentPadding = PaddingValues(12.dp),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colorScheme.primary,
containerColor = MaterialTheme.colorScheme.background
),
onClick = { viewModel.addRepository() }
) {
Text(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.add_repository),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleSmall
)
Icon(
painter = painterResource(id = R.drawable.ic_add),
contentDescription = stringResource(id = R.string.add_repository)
)
}
RepositoriesRecycler(
repositoriesList = repos?.sortedBy { repo -> !repo.enabled },
onClick = { repo ->
GlobalScope.launch(Dispatchers.IO) {
syncConnection.binder?.setEnabled(repo, repo.enabled)
}
},
onLongClick = { repo ->
RepositorySheetX(repo.id)
.showNow(parentFragmentManager, "Repository ${repo.id}")
})
}
}
} }
} }
} }

View File

@ -1,26 +1,56 @@
package com.looker.droidify.ui.viewmodels package com.looker.droidify.ui.viewmodels
import android.content.Context
import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.looker.droidify.database.DatabaseX import com.looker.droidify.database.dao.RepositoryDao
import com.looker.droidify.database.entity.Repository import com.looker.droidify.database.entity.Repository
import com.looker.droidify.database.entity.Repository.Companion.newRepository import com.looker.droidify.database.entity.Repository.Companion.newRepository
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class RepositoriesViewModelX(val db: DatabaseX) : ViewModel() { class RepositoriesViewModelX(val repositoryDao: RepositoryDao) : ViewModel() {
val repositories: MediatorLiveData<List<Repository>> = MediatorLiveData()
val toLaunch: MediatorLiveData<Pair<Boolean, Long>?> = MediatorLiveData() val toLaunch: MediatorLiveData<Pair<Boolean, Long>?> = MediatorLiveData()
val syncConnection = Connection(SyncService::class.java)
private val _showSheet = MutableStateFlow<Long?>(null)
val showSheet = _showSheet.asStateFlow()
private val _repositories = MutableStateFlow<List<Repository>>(emptyList())
val repositories = _repositories.asStateFlow()
init { init {
repositories.addSource(db.repositoryDao.allLive, repositories::setValue) viewModelScope.launch(Dispatchers.IO) {
_repositories.emit(repositoryDao.all)
}
toLaunch.value = null toLaunch.value = null
} }
fun bindConnection(context: Context) {
viewModelScope.launch { syncConnection.bind(context) }
}
fun showRepositorySheet(repositoryId: Long) {
viewModelScope.launch {
_showSheet.emit(repositoryId)
}
}
fun toggleRepository(repository: Repository, isEnabled: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
syncConnection.binder?.setEnabled(repository, isEnabled)
}
}
fun addRepository() { fun addRepository() {
viewModelScope.launch { viewModelScope.launch {
toLaunch.value = Pair(true, addNewRepository()) toLaunch.value = Pair(true, addNewRepository())
@ -28,19 +58,19 @@ class RepositoriesViewModelX(val db: DatabaseX) : ViewModel() {
} }
private suspend fun addNewRepository(): Long = withContext(Dispatchers.IO) { private suspend fun addNewRepository(): Long = withContext(Dispatchers.IO) {
db.repositoryDao.insert(newRepository(address = "new.Repository")) repositoryDao.insert(newRepository())
db.repositoryDao.latestAddedId() repositoryDao.latestAddedId()
} }
fun emptyToLaunch() { fun emptyToLaunch() {
toLaunch.value = null toLaunch.value = null
} }
class Factory(val db: DatabaseX) : ViewModelProvider.Factory { class Factory(private val repoDao: RepositoryDao) : ViewModelProvider.Factory {
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(RepositoriesViewModelX::class.java)) { if (modelClass.isAssignableFrom(RepositoriesViewModelX::class.java)) {
return RepositoriesViewModelX(db) as T return RepositoriesViewModelX(repoDao) as T
} }
throw IllegalArgumentException("Unknown ViewModel class") throw IllegalArgumentException("Unknown ViewModel class")
} }