Add TimelineItem large layout base

This commit is contained in:
Shinokuni 2023-08-14 22:40:29 +02:00
parent 8a04fad0d7
commit b15eb9fa91
6 changed files with 167 additions and 73 deletions

View File

@ -103,5 +103,6 @@ dependencies {
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
implementation "com.github.skydoves:landscapist-coil:2.2.2"
implementation "io.coil-kt:coil:2.4.0"
implementation "io.coil-kt:coil-compose:2.4.0"
}

View File

@ -9,9 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil.request.ImageRequest
import com.readrops.db.entities.Feed
import com.skydoves.landscapist.coil.CoilImage
@Composable
fun FeedItem(feed: Feed) {
@ -22,11 +20,11 @@ fun FeedItem(feed: Feed) {
.fillMaxWidth()
.padding(8.dp)
) {
CoilImage(imageRequest = {
/*CoilImage(imageRequest = {
ImageRequest.Builder(context)
.data(feed.url)
.build()
})
})*/
Text(
text = feed.name!!,

View File

@ -1,73 +1,152 @@
package com.readrops.app.compose.timelime
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.readrops.api.utils.DateUtils
import com.readrops.app.compose.R
import com.readrops.app.compose.utils.theme.ShortSpacer
import com.readrops.app.compose.utils.theme.VeryShortSpacer
import com.readrops.app.compose.utils.theme.spacing
import com.readrops.db.pojo.ItemWithFeed
@Preview
@Composable
fun TimelineItem(
onClick: () -> Unit,
itemWithFeed: ItemWithFeed,
onClick: () -> Unit,
onFavorite: () -> Unit,
onReadLater: () -> Unit,
onShare: () -> Unit
) {
Card(
// elevation = 4.card,
modifier = Modifier.background(Color.White)
.padding(8.dp)
.clickable { onClick() }
modifier = Modifier
.padding(
PaddingValues(
horizontal = MaterialTheme.spacing.shortSpacing,
vertical = MaterialTheme.spacing.veryShortSpacing
)
)
.clickable { onClick() }
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.padding(
start = MaterialTheme.spacing.shortSpacing,
end = MaterialTheme.spacing.shortSpacing,
top = MaterialTheme.spacing.shortSpacing,
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
/* Icon(
painter = painterResource(id = com.readrops.app.R.drawable.ic_rss_feed_grey),
Icon(
painter = painterResource(id = R.drawable.ic_rss_feed_grey),
contentDescription = null,
// modifier = Modifier.size((MaterialTheme.typography.subtitle2.fontSize.value * 1.5).dp)
)*/
// Spacer(Modifier.padding(4.dp))
Text(
text = "feed name",
//style = MaterialTheme.typography.
modifier = Modifier.size(MaterialTheme.typography.labelLarge.fontSize.value.dp)
)
VeryShortSpacer()
Column {
Text(
text = itemWithFeed.feedName,
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
VeryShortSpacer()
if (itemWithFeed.folder != null) {
Text(
text = itemWithFeed.folder!!.name!!,
style = MaterialTheme.typography.labelMedium
)
}
}
}
Text(
text = "Item date",
// style = MaterialTheme.typography.subtitle2
text = DateUtils.formattedDateByLocal(itemWithFeed.item.pubDate!!),
style = MaterialTheme.typography.labelMedium,
)
}
Spacer(Modifier.size(8.dp))
ShortSpacer()
Text(
text = "title example",
//style = MaterialTheme.typography.h5,
text = itemWithFeed.item.title!!,
style = MaterialTheme.typography.titleMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.shortSpacing)
)
Spacer(Modifier.size(8.dp))
ShortSpacer()
/* Image(
painter = painterResource(id = com.readrops.app.R.drawable.header_background),
contentDescription = null
)*/
if (itemWithFeed.item.imageLink != null) {
AsyncImage(
model = itemWithFeed.item.imageLink,
contentDescription = itemWithFeed.item.title!!,
contentScale = ContentScale.Crop,
modifier = Modifier
.aspectRatio(3f / 2f)
.fillMaxWidth()
)
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(MaterialTheme.spacing.shortSpacing)
) {
Icon(
imageVector = Icons.Outlined.FavoriteBorder,
contentDescription = null,
modifier = Modifier.clickable { onFavorite() }
)
Icon(
imageVector = Icons.Outlined.Add, // placeholder icon
contentDescription = null,
modifier = Modifier.clickable { onReadLater() }
)
Icon(
imageVector = Icons.Outlined.Share,
contentDescription = null,
modifier = Modifier.clickable { onShare() }
)
}
}
}
}

View File

@ -34,8 +34,8 @@ object TimelineTab : Tab {
@Composable
get() {
return TabOptions(
index = 1u,
title = "Timeline",
index = 1u,
title = "Timeline",
)
}
@ -51,30 +51,35 @@ object TimelineTab : Tab {
val navigator = LocalNavigator.currentOrThrow
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Timeline") }
)
}
topBar = {
TopAppBar(
title = { Text(text = "Timeline") }
)
}
) { paddingValues ->
SwipeRefresh(
state = swipeToRefreshState,
onRefresh = {
viewModel.refreshTimeline()
},
modifier = Modifier.padding(paddingValues)
state = swipeToRefreshState,
onRefresh = {
viewModel.refreshTimeline()
},
modifier = Modifier.padding(paddingValues)
) {
when (state) {
is TimelineState.LoadedState -> {
val items = (state as TimelineState.LoadedState).items
is TimelineState.Loaded -> {
val items = (state as TimelineState.Loaded).items
if (items.isNotEmpty()) {
LazyColumn {
items(
items = items
) {
items = items,
key = { it.item.id },
) { itemWithFeed ->
TimelineItem(
onClick = { navigator.push(ItemScreen()) }
itemWithFeed = itemWithFeed,
onClick = { navigator.push(ItemScreen()) },
onFavorite = {},
onReadLater = {},
onShare = {},
)
}
}
@ -99,15 +104,15 @@ fun NoItemPlaceholder() {
val scrollState = rememberScrollState()
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {
Text(
text = "No item",
style = MaterialTheme.typography.displayMedium
text = "No item",
style = MaterialTheme.typography.displayMedium
)
}
}

View File

@ -3,7 +3,9 @@ package com.readrops.app.compose.timelime
import androidx.lifecycle.viewModelScope
import com.readrops.app.compose.base.TabViewModel
import com.readrops.db.Database
import com.readrops.db.entities.Item
import com.readrops.db.pojo.ItemWithFeed
import com.readrops.db.queries.ItemsQueryBuilder
import com.readrops.db.queries.QueryFilters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -14,7 +16,7 @@ class TimelineViewModel(
private val database: Database,
) : TabViewModel(database) {
private val _timelineState = MutableStateFlow<TimelineState>(TimelineState.InitialState)
private val _timelineState = MutableStateFlow<TimelineState>(TimelineState.Loading)
val timelineState = _timelineState.asStateFlow()
private var _isRefreshing = MutableStateFlow(false)
@ -22,10 +24,12 @@ class TimelineViewModel(
init {
viewModelScope.launch(context = Dispatchers.IO) {
database.newItemDao().selectAll()
.catch { _timelineState.value = TimelineState.ErrorState(Exception(it)) }
val query = ItemsQueryBuilder.buildItemsQuery(QueryFilters(accountId = 1))
database.newItemDao().selectAll(query)
.catch { _timelineState.value = TimelineState.Error(Exception(it)) }
.collect {
_timelineState.value = TimelineState.LoadedState(it)
_timelineState.value = TimelineState.Loaded(it)
}
}
}
@ -42,13 +46,13 @@ class TimelineViewModel(
}
override fun invalidate() {
refreshTimeline()
}
}
sealed class TimelineState {
object InitialState : TimelineState()
data class ErrorState(val exception: Exception) : TimelineState()
data class LoadedState(val items: List<Item>) : TimelineState()
object Loading : TimelineState()
data class Error(val exception: Exception) : TimelineState()
data class Loaded(val items: List<ItemWithFeed>) : TimelineState()
}

View File

@ -1,13 +1,20 @@
package com.readrops.db.dao.newdao
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SupportSQLiteQuery
import com.readrops.db.entities.Feed
import com.readrops.db.entities.Folder
import com.readrops.db.entities.Item
import com.readrops.db.entities.ItemState
import com.readrops.db.pojo.ItemWithFeed
import kotlinx.coroutines.flow.Flow
@Dao
abstract class NewItemDao : NewBaseDao<Item> {
@Query("Select * From Item")
abstract fun selectAll(): Flow<List<Item>>
@RawQuery(observedEntities = [Item::class, Feed::class, Folder::class, ItemState::class])
abstract fun selectAll(query: SupportSQLiteQuery): Flow<List<ItemWithFeed>>
}