Merge pull request #31 from msfjarvis/offline-cache

Set up offline caching and no posts state
This commit is contained in:
probot-auto-merge[bot] 2020-09-27 11:44:40 +00:00 committed by GitHub
commit 0ecf21467b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 13 deletions

View file

@ -4,6 +4,9 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumnForIndexed
import androidx.compose.material.Scaffold
import androidx.compose.material.TopAppBar
@ -11,10 +14,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
import androidx.compose.runtime.ambientOf
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.res.stringResource
import dagger.hilt.android.AndroidEntryPoint
import dev.msfjarvis.lobsters.api.LobstersApi
import dev.msfjarvis.lobsters.compose.utils.IconResource
import dev.msfjarvis.lobsters.data.LobstersViewModel
import dev.msfjarvis.lobsters.ui.LobstersItem
import dev.msfjarvis.lobsters.ui.LobstersTheme
@ -52,15 +58,26 @@ fun LobstersApp(
Scaffold(
topBar = { TopAppBar({ Text(text = stringResource(R.string.app_name)) }) },
bodyContent = {
LazyColumnForIndexed(state.value) { index, item ->
if (lastIndex == index) {
viewModel.getMorePosts()
if (state.value.isEmpty()) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
IconResource(R.drawable.ic_sync_problem_24px)
Text(stringResource(R.string.nothing_to_see_here))
}
} else {
LazyColumnForIndexed(state.value) { index, item ->
if (lastIndex == index) {
viewModel.getMorePosts()
}
LobstersItem(
item,
linkOpenAction = { post -> urlLauncher.launch(post.url) },
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
)
}
LobstersItem(
item,
linkOpenAction = { post -> urlLauncher.launch(post.url) },
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
)
}
}
)

View file

@ -4,27 +4,44 @@ import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dev.msfjarvis.lobsters.api.LobstersApi
import dev.msfjarvis.lobsters.data.source.PostsDatabase
import dev.msfjarvis.lobsters.model.LobstersPost
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.net.UnknownHostException
class LobstersViewModel @ViewModelInject constructor(
private val lobstersApi: LobstersApi,
database: PostsDatabase,
) : ViewModel() {
private var apiPage = 1
private val _posts = MutableStateFlow<List<LobstersPost>>(emptyList())
private val dao = database.postsDao()
private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
when (throwable) {
// Swallow UHE since that happens when there is no internet and we'll just rely on our cache
is UnknownHostException -> {}
else -> throw throwable
}
}
val posts: StateFlow<List<LobstersPost>> get() = _posts
init {
viewModelScope.launch {
dao.loadPosts().collectLatest { _posts.value = it }
}
getMorePosts()
}
fun getMorePosts() {
viewModelScope.launch {
_posts.value += lobstersApi.getHottestPosts(apiPage)
viewModelScope.launch(coroutineExceptionHandler) {
val newPosts = lobstersApi.getHottestPosts(apiPage)
_posts.value += newPosts
apiPage += 1
dao.insertPosts(*_posts.value.toTypedArray())
}
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M3,12c0,2.21 0.91,4.2 2.36,5.64l-1.51,1.51c-0.31,0.31 -0.09,0.85 0.36,0.85L8.5,20c0.28,0 0.5,-0.22 0.5,-0.5v-4.29c0,-0.45 -0.54,-0.67 -0.85,-0.35l-1.39,1.39C5.68,15.15 5,13.66 5,12c0,-2.39 1.4,-4.46 3.43,-5.42 0.34,-0.16 0.57,-0.47 0.57,-0.84v-0.19c0,-0.68 -0.71,-1.11 -1.32,-0.82C4.92,5.99 3,8.77 3,12zM11,17h2v-2h-2v2zM19.79,4L15.5,4c-0.28,0 -0.5,0.22 -0.5,0.5v4.29c0,0.45 0.54,0.67 0.85,0.35l1.39,-1.39C18.32,8.85 19,10.34 19,12c0,2.39 -1.4,4.46 -3.43,5.42 -0.34,0.16 -0.57,0.47 -0.57,0.84v0.18c0,0.68 0.71,1.11 1.32,0.82C19.08,18.01 21,15.23 21,12c0,-2.21 -0.91,-4.2 -2.36,-5.64l1.51,-1.51c0.31,-0.31 0.09,-0.85 -0.36,-0.85zM12,13c0.55,0 1,-0.45 1,-1L13,8c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4c0,0.55 0.45,1 1,1z"/>
</vector>

View file

@ -1,3 +1,4 @@
<resources>
<string name="app_name">lobste.rs</string>
</resources>
<string name="nothing_to_see_here">Nothing to see here…</string>
</resources>

View file

@ -3,6 +3,7 @@ package dev.msfjarvis.lobsters.data.source
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import dev.msfjarvis.lobsters.data.model.LobstersEntity
@ -19,7 +20,7 @@ abstract class PostsDao {
insertPosts(posts.map { LobstersEntity(it) })
}
@Insert
@Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract suspend fun insertPosts(posts: List<LobstersEntity>)
@Transaction