diff --git a/appcompose/build.gradle b/appcompose/build.gradle index 6356c382..84c07e05 100644 --- a/appcompose/build.gradle +++ b/appcompose/build.gradle @@ -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" } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/feeds/FeedItem.kt b/appcompose/src/main/java/com/readrops/app/compose/feeds/FeedItem.kt index d4ecfe5f..0ea04f2b 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/feeds/FeedItem.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/feeds/FeedItem.kt @@ -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!!, diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineItem.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineItem.kt index f93eda0f..eabc87bf 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineItem.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineItem.kt @@ -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() } + ) + } } } } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt index 10536ffe..59b0d2bf 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineTab.kt @@ -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 ) } } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineViewModel.kt b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineViewModel.kt index 8eed245f..373ffd4c 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineViewModel.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/timelime/TimelineViewModel.kt @@ -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.InitialState) + private val _timelineState = MutableStateFlow(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) : TimelineState() + object Loading : TimelineState() + data class Error(val exception: Exception) : TimelineState() + data class Loaded(val items: List) : TimelineState() } diff --git a/db/src/main/java/com/readrops/db/dao/newdao/NewItemDao.kt b/db/src/main/java/com/readrops/db/dao/newdao/NewItemDao.kt index 561c874c..7d9e97eb 100644 --- a/db/src/main/java/com/readrops/db/dao/newdao/NewItemDao.kt +++ b/db/src/main/java/com/readrops/db/dao/newdao/NewItemDao.kt @@ -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 { - @Query("Select * From Item") - abstract fun selectAll(): Flow> + @RawQuery(observedEntities = [Item::class, Feed::class, Folder::class, ItemState::class]) + abstract fun selectAll(query: SupportSQLiteQuery): Flow> + + } \ No newline at end of file