Add pull to refresh on entry list view
This commit is contained in:
parent
2d02416101
commit
ed0a7d813f
11 changed files with 83 additions and 49 deletions
|
@ -24,6 +24,8 @@
|
|||
<entry key="../../../../layout/compose-model-1663094305778.xml" value="0.1" />
|
||||
<entry key="../../../../layout/compose-model-1663094326622.xml" value="0.1" />
|
||||
<entry key="../../../../layout/compose-model-1663094326625.xml" value="1.0" />
|
||||
<entry key="../../../../layout/compose-model-1663101323996.xml" value="0.1" />
|
||||
<entry key="../../../../layout/compose-model-1663188478804.xml" value="0.47846283783783783" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
@ -57,6 +57,7 @@ dependencies {
|
|||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.accompanist.swiperefresh)
|
||||
implementation(libs.bundles.compose)
|
||||
implementation(libs.lifecycle)
|
||||
implementation(libs.hilt.android.core)
|
||||
|
|
|
@ -6,15 +6,10 @@ import com.wbrawner.nanoflux.network.repository.CategoryRepository
|
|||
import com.wbrawner.nanoflux.network.repository.EntryRepository
|
||||
import com.wbrawner.nanoflux.network.repository.FeedRepository
|
||||
import com.wbrawner.nanoflux.network.repository.IconRepository
|
||||
import com.wbrawner.nanoflux.storage.model.Entry
|
||||
import com.wbrawner.nanoflux.storage.model.EntryAndFeed
|
||||
import com.wbrawner.nanoflux.syncAll
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -28,11 +23,9 @@ class MainViewModel @Inject constructor(
|
|||
) : ViewModel() {
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (entryRepository.getCount() == 0L) {
|
||||
syncAll(categoryRepository, feedRepository, iconRepository, entryRepository)
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (entryRepository.getCount() == 0L) {
|
||||
syncAll(categoryRepository, feedRepository, iconRepository, entryRepository)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,12 @@ class UnreadViewModel @Inject constructor(
|
|||
|
||||
// TODO: Get Base URL
|
||||
fun getShareUrl(entry: Entry) = "baseUrl/${entry.shareCode}"
|
||||
|
||||
fun refresh() = viewModelScope.launch(Dispatchers.IO) {
|
||||
_loading.emit(true)
|
||||
feedRepository.getAll(fetch = true)
|
||||
categoryRepository.getAll(fetch = true)
|
||||
entryRepository.getAll(fetch = true)
|
||||
_loading.emit(false)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import androidx.compose.ui.graphics.asImageBitmap
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import com.wbrawner.nanoflux.NanofluxApp
|
||||
import com.wbrawner.nanoflux.storage.model.*
|
||||
import java.util.*
|
||||
|
@ -32,20 +34,26 @@ fun EntryList(
|
|||
onFeedClicked: (feed: Feed) -> Unit,
|
||||
onToggleReadClicked: (entry: Entry) -> Unit,
|
||||
onStarClicked: (entry: Entry) -> Unit,
|
||||
onExternalLinkClicked: (entry: Entry) -> Unit
|
||||
onExternalLinkClicked: @Composable (entry: Entry) -> Unit,
|
||||
isRefreshing: Boolean,
|
||||
onRefresh: () -> Unit
|
||||
) {
|
||||
// TODO: Add pull to refresh
|
||||
LazyColumn {
|
||||
items(entries) { entry ->
|
||||
EntryListItem(
|
||||
entry.entry,
|
||||
entry.feed,
|
||||
onEntryItemClicked,
|
||||
onFeedClicked,
|
||||
onToggleReadClicked,
|
||||
onStarClicked,
|
||||
onExternalLinkClicked
|
||||
)
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing = isRefreshing),
|
||||
onRefresh = onRefresh
|
||||
) {
|
||||
LazyColumn {
|
||||
items(entries) { entry ->
|
||||
EntryListItem(
|
||||
entry.entry,
|
||||
entry.feed,
|
||||
onEntryItemClicked,
|
||||
onFeedClicked,
|
||||
onToggleReadClicked,
|
||||
onStarClicked,
|
||||
onExternalLinkClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +66,7 @@ fun EntryListItem(
|
|||
onFeedClicked: (feed: Feed) -> Unit,
|
||||
onToggleReadClicked: (entry: Entry) -> Unit,
|
||||
onStarClicked: (entry: Entry) -> Unit,
|
||||
onExternalLinkClicked: (entry: Entry) -> Unit
|
||||
onExternalLinkClicked: @Composable (entry: Entry) -> Unit
|
||||
) {
|
||||
// val swipeState = rememberSwipeableState(initialValue = entry.status)
|
||||
Column(
|
||||
|
@ -121,13 +129,15 @@ fun EntryListItem(
|
|||
}
|
||||
val context = LocalContext.current
|
||||
IconButton(onClick = {
|
||||
context.startActivity(Intent(Intent.ACTION_SEND).apply {
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
// TODO: Get base url from viewmodel or something
|
||||
type = "text/plain"
|
||||
putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
"https://wbrawner.com/entry/share/${entry.shareCode}"
|
||||
)
|
||||
})
|
||||
}
|
||||
context.startActivity(Intent.createChooser(intent, null))
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
|
|
|
@ -109,6 +109,12 @@ fun MainScaffold(
|
|||
composable("unread") {
|
||||
UnreadScreen(navController, snackbarHostState, hiltViewModel())
|
||||
}
|
||||
composable("starred") {
|
||||
UnreadScreen(navController, snackbarHostState, hiltViewModel())
|
||||
}
|
||||
composable("history") {
|
||||
UnreadScreen(navController, snackbarHostState, hiltViewModel())
|
||||
}
|
||||
composable(
|
||||
"entries/{entryId}",
|
||||
arguments = listOf(navArgument("entryId") { type = NavType.LongType })
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package com.wbrawner.nanoflux.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.wbrawner.nanoflux.data.viewmodel.UnreadViewModel
|
||||
|
@ -17,30 +20,30 @@ fun UnreadScreen(
|
|||
val errorMessage by unreadViewModel.errorMessage.collectAsState()
|
||||
val entries by unreadViewModel.entries.collectAsState(emptyList())
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
if (loading) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
errorMessage?.let {
|
||||
coroutineScope.launch {
|
||||
when (snackbarHostState.showSnackbar(it, "Retry")) {
|
||||
// SnackbarResult.ActionPerformed -> unreadViewModel.loadUnread()
|
||||
SnackbarResult.ActionPerformed -> unreadViewModel.refresh()
|
||||
else -> unreadViewModel.dismissError()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entries.isEmpty()) {
|
||||
Text("TODO: No entries")
|
||||
} else {
|
||||
EntryList(
|
||||
entries = entries,
|
||||
onEntryItemClicked = {
|
||||
navController.navigate("entries/${it.id}")
|
||||
},
|
||||
onFeedClicked = {
|
||||
navController.navigate("feeds/${it.id}")
|
||||
},
|
||||
onToggleReadClicked = { /*TODO*/ },
|
||||
onStarClicked = { /*TODO*/ }) {
|
||||
EntryList(
|
||||
entries = entries,
|
||||
onEntryItemClicked = {
|
||||
navController.navigate("entries/${it.id}")
|
||||
},
|
||||
onFeedClicked = {
|
||||
navController.navigate("feeds/${it.id}")
|
||||
},
|
||||
onToggleReadClicked = { /*TODO*/ },
|
||||
onStarClicked = { /*TODO*/ },
|
||||
onExternalLinkClicked = {
|
||||
LocalContext.current.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it.url)))
|
||||
},
|
||||
isRefreshing = loading,
|
||||
onRefresh = {
|
||||
unreadViewModel.refresh()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -18,6 +18,7 @@ versionName = "1.0"
|
|||
work = "2.7.1"
|
||||
|
||||
[libraries]
|
||||
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version = "0.26.3-beta"}
|
||||
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
|
|
|
@ -14,11 +14,11 @@ class EntryRepository @Inject constructor(
|
|||
private val entryDao: EntryDao,
|
||||
private val logger: Timber.Tree
|
||||
) {
|
||||
fun observeUnread(): Flow<List<EntryAndFeed>> = entryDao.observeAll()
|
||||
fun observeUnread(): Flow<List<EntryAndFeed>> = entryDao.observeUnread()
|
||||
|
||||
fun getCount(): Long = entryDao.getCount()
|
||||
|
||||
suspend fun getAll(fetch: Boolean = false, afterId: Long = 0): List<EntryAndFeed> {
|
||||
suspend fun getAll(fetch: Boolean = false, afterId: Long? = null): List<EntryAndFeed> {
|
||||
if (fetch) {
|
||||
getEntries { page ->
|
||||
apiService.getEntries(
|
||||
|
@ -76,4 +76,10 @@ class EntryRepository @Inject constructor(
|
|||
totalPages = response.total / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class EntryStatus {
|
||||
UNREAD,
|
||||
STARRED,
|
||||
HISTORY
|
||||
}
|
|
@ -11,11 +11,15 @@ interface EntryDao {
|
|||
fun getCount(): Long
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Entry ORDER BY createdAt DESC")
|
||||
@Query("SELECT * FROM Entry ORDER BY publishedAt DESC")
|
||||
fun observeAll(): Flow<List<EntryAndFeed>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Entry ORDER BY createdAt DESC")
|
||||
@Query("SELECT * FROM Entry WHERE status = \"UNREAD\" ORDER BY publishedAt DESC")
|
||||
fun observeUnread(): Flow<List<EntryAndFeed>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Entry ORDER BY publishedAt DESC")
|
||||
fun getAll(): List<EntryAndFeed>
|
||||
|
||||
@Transaction
|
||||
|
|
|
@ -7,7 +7,7 @@ import com.wbrawner.nanoflux.storage.model.FeedCategoryIcon
|
|||
@Dao
|
||||
interface FeedDao {
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Feed")
|
||||
@Query("SELECT * FROM Feed ORDER BY title ASC")
|
||||
fun getAll(): List<FeedCategoryIcon>
|
||||
|
||||
@Transaction
|
||||
|
|
Loading…
Reference in a new issue