diff --git a/app/build.gradle b/app/build.gradle
index d4d4bb5..a6ba043 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -28,6 +28,15 @@ android {
vectorDrawables {
useSupportLibrary true
}
+
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments += [
+ "room.schemaLocation": "$projectDir/schemas".toString(),
+ "room.incremental" : "true"
+ ]
+ }
+ }
}
flavorDimensions "channel"
@@ -103,6 +112,8 @@ dependencies {
implementation("io.coil-kt:coil-svg:$coil")
implementation("io.coil-kt:coil-gif:$coil")
+ implementation "org.conscrypt:conscrypt-android:2.5.2"
+
// https://square.github.io/okhttp/changelogs/changelog/
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.6"
implementation "com.squareup.retrofit2:retrofit:$retrofit2"
diff --git a/app/schemas/me.ash.reader.data.source.ReaderDatabase/2.json b/app/schemas/me.ash.reader.data.source.ReaderDatabase/2.json
new file mode 100644
index 0000000..5690ca5
--- /dev/null
+++ b/app/schemas/me.ash.reader.data.source.ReaderDatabase/2.json
@@ -0,0 +1,321 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "98462c2e9c32394054102313366e7262",
+ "entities": [
+ {
+ "tableName": "account",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `type` INTEGER NOT NULL, `updateAt` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "updateAt",
+ "columnName": "updateAt",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "feed",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT, `url` TEXT NOT NULL, `groupId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isNotification` INTEGER NOT NULL DEFAULT false, `isFullContent` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`), FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "icon",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "groupId",
+ "columnName": "groupId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotification",
+ "columnName": "isNotification",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isFullContent",
+ "columnName": "isFullContent",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_feed_groupId",
+ "unique": false,
+ "columnNames": [
+ "groupId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_groupId` ON `${TABLE_NAME}` (`groupId`)"
+ },
+ {
+ "name": "index_feed_accountId",
+ "unique": false,
+ "columnNames": [
+ "accountId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_accountId` ON `${TABLE_NAME}` (`accountId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "group",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "groupId"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "article",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `date` INTEGER NOT NULL, `title` TEXT NOT NULL, `author` TEXT, `rawDescription` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `fullContent` TEXT, `img` TEXT, `link` TEXT NOT NULL, `feedId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isUnread` INTEGER NOT NULL DEFAULT true, `isStarred` INTEGER NOT NULL DEFAULT false, `isReadLater` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`), FOREIGN KEY(`feedId`) REFERENCES `feed`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "rawDescription",
+ "columnName": "rawDescription",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortDescription",
+ "columnName": "shortDescription",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullContent",
+ "columnName": "fullContent",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "img",
+ "columnName": "img",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "link",
+ "columnName": "link",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "feedId",
+ "columnName": "feedId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isUnread",
+ "columnName": "isUnread",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "true"
+ },
+ {
+ "fieldPath": "isStarred",
+ "columnName": "isStarred",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isReadLater",
+ "columnName": "isReadLater",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_article_feedId",
+ "unique": false,
+ "columnNames": [
+ "feedId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_article_feedId` ON `${TABLE_NAME}` (`feedId`)"
+ },
+ {
+ "name": "index_article_accountId",
+ "unique": false,
+ "columnNames": [
+ "accountId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_article_accountId` ON `${TABLE_NAME}` (`accountId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "feed",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "feedId"
+ ],
+ "referencedColumns": [
+ "id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "group",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `accountId` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_group_accountId",
+ "unique": false,
+ "columnNames": [
+ "accountId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_group_accountId` ON `${TABLE_NAME}` (`accountId`)"
+ }
+ ],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '98462c2e9c32394054102313366e7262')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 648c8bf..d275ec0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,18 +5,18 @@
-
-
+
+
+ android:theme="@style/Theme.Reader"
+ android:usesCleartextTraffic="true">
): List
@Update
suspend fun update(vararg article: Article)
- @Delete
- suspend fun delete(vararg article: Article)
-
@RewriteQueriesToDropUnusedColumns
@Transaction
@Query(
"""
INSERT INTO article
SELECT :id, :date, :title, :author, :rawDescription,
- :shortDescription, :fullContent, :link, :feedId,
+ :shortDescription, :fullContent, :img, :link, :feedId,
:accountId, :isUnread, :isStarred, :isReadLater
WHERE NOT EXISTS(SELECT 1 FROM article WHERE link = :link AND accountId = :accountId)
"""
@@ -534,6 +528,7 @@ interface ArticleDao {
rawDescription: String,
shortDescription: String,
fullContent: String? = null,
+ img: String? = null,
link: String,
feedId: String,
accountId: Int,
@@ -552,6 +547,7 @@ interface ArticleDao {
article.rawDescription,
article.shortDescription,
article.fullContent,
+ article.img,
article.link,
article.feedId,
article.accountId,
diff --git a/app/src/main/java/me/ash/reader/data/entity/Article.kt b/app/src/main/java/me/ash/reader/data/entity/Article.kt
index e80cff0..42845e0 100644
--- a/app/src/main/java/me/ash/reader/data/entity/Article.kt
+++ b/app/src/main/java/me/ash/reader/data/entity/Article.kt
@@ -32,6 +32,8 @@ data class Article(
@ColumnInfo
var fullContent: String? = null,
@ColumnInfo
+ var img: String? = null,
+ @ColumnInfo
val link: String,
@ColumnInfo(index = true)
val feedId: String,
diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
index 078bd05..149ec8c 100644
--- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
+++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt
@@ -116,13 +116,23 @@ class RssHelper @Inject constructor(
.trim(),
fullContent = content,
link = it.link ?: "",
- )
+ ).apply {
+ img = findImg(rawDescription)
+ }
)
}
a
}
}
+ private fun findImg(rawDescription: String): String? {
+ // From: https://gitlab.com/spacecowboy/Feeder
+ // Using negative lookahead to skip data: urls, being inline base64
+ // And capturing original quote to use as ending quote
+ val regex = """img.*?src=(["'])((?!data).*?)\1""".toRegex(RegexOption.DOT_MATCHES_ALL)
+ return regex.find(rawDescription)?.groupValues?.get(2)
+ }
+
@Throws(Exception::class)
suspend fun queryRssIcon(
feedDao: FeedDao,
diff --git a/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt b/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt
index 76a4734..2fa0971 100644
--- a/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt
+++ b/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt
@@ -2,6 +2,8 @@ package me.ash.reader.data.source
import android.content.Context
import androidx.room.*
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
import me.ash.reader.data.dao.AccountDao
import me.ash.reader.data.dao.ArticleDao
import me.ash.reader.data.dao.FeedDao
@@ -14,8 +16,7 @@ import java.util.*
@Database(
entities = [Account::class, Feed::class, Article::class, Group::class],
- version = 1,
- exportSchema = false,
+ version = 2,
)
@TypeConverters(ReaderDatabase.Converters::class)
abstract class ReaderDatabase : RoomDatabase() {
@@ -33,7 +34,7 @@ abstract class ReaderDatabase : RoomDatabase() {
context.applicationContext,
ReaderDatabase::class.java,
"Reader"
- ).build().also {
+ ).addMigrations(*allMigrations).build().also {
instance = it
}
}
@@ -52,4 +53,19 @@ abstract class ReaderDatabase : RoomDatabase() {
return date?.time
}
}
+}
+
+val allMigrations = arrayOf(
+ MIGRATION_1_2,
+)
+
+@Suppress("ClassName")
+object MIGRATION_1_2 : Migration(1, 2) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL(
+ """
+ ALTER TABLE article ADD COLUMN img TEXT DEFAULT NULL
+ """.trimIndent()
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt
index 1e06009..fd9a2fc 100644
--- a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt
+++ b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt
@@ -3,6 +3,9 @@ package me.ash.reader.ui.component
import androidx.annotation.DrawableRes
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
@@ -31,6 +34,31 @@ fun AsyncImage(
@DrawableRes error: Int? = R.drawable.ic_broken_image_black_24dp,
) {
val context = LocalContext.current
+ val color = MaterialTheme.colorScheme.onSurfaceVariant
+ val placeholderPainterResource = placeholder?.run { painterResource(this) }
+ val errorPainterResource = error?.run { painterResource(this) }
+ val placeholderPainter by remember {
+ mutableStateOf(
+ placeholderPainterResource?.run {
+ forwardingPainter(
+ painter = this,
+ colorFilter = ColorFilter.tint(color),
+ alpha = 0.1f,
+ )
+ }
+ )
+ }
+ val errorPainter by remember {
+ mutableStateOf(
+ errorPainterResource?.run {
+ forwardingPainter(
+ painter = this,
+ colorFilter = ColorFilter.tint(color),
+ alpha = 0.1f,
+ )
+ }
+ )
+ }
coil.compose.AsyncImage(
modifier = modifier,
@@ -45,20 +73,8 @@ fun AsyncImage(
contentDescription = contentDescription,
contentScale = contentScale,
imageLoader = context.imageLoader,
- placeholder = placeholder?.let {
- forwardingPainter(
- painter = painterResource(it),
- colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant),
- alpha = 0.5f,
- )
- },
- error = error?.let {
- forwardingPainter(
- painter = painterResource(it),
- colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onError),
- alpha = 0.5f
- )
- },
+ placeholder = placeholderPainter,
+ error = errorPainter,
)
}
diff --git a/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt b/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt
index a991e8a..ca9eb77 100644
--- a/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt
+++ b/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt
@@ -1,6 +1,9 @@
package me.ash.reader.ui.ext
import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.paging.compose.LazyPagingItems
import kotlin.math.abs
fun LazyListState.calculateTopBarAnimateValue(start: Float, end: Float): Float =
@@ -12,3 +15,16 @@ fun LazyListState.calculateTopBarAnimateValue(start: Float, end: Float): Float =
if (start < end) (start + increase).coerceIn(start, end)
else (start - increase).coerceIn(end, start)
}
+
+@Composable
+fun LazyPagingItems.rememberLazyListState(): LazyListState {
+ // After recreation, LazyPagingItems first return 0 items, then the cached items.
+ // This behavior/issue is resetting the LazyListState scroll position.
+ // Below is a workaround. More info: https://issuetracker.google.com/issues/177245496.
+ return when (itemCount) {
+ // Return a different LazyListState instance.
+ 0 -> remember(this) { LazyListState(0, 0) }
+ // Return rememberLazyListState (normal case).
+ else -> androidx.compose.foundation.lazy.rememberLazyListState()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
index 88a2ccd..87b5015 100644
--- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
@@ -9,7 +9,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.paging.compose.collectAsLazyPagingItems
import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@@ -38,11 +37,7 @@ fun HomeEntry(
homeViewModel: HomeViewModel = hiltViewModel(),
) {
val context = LocalContext.current
-
- val viewState = homeViewModel.viewState.collectAsStateValue()
val filterState = homeViewModel.filterState.collectAsStateValue()
- val pagingItems = viewState.pagingData.collectAsLazyPagingItems()
-
val navController = rememberAnimatedNavController()
val intent by rememberSaveable { mutableStateOf(context.findActivity()?.intent) }
@@ -116,7 +111,6 @@ fun HomeEntry(
FlowPage(
navController = navController,
homeViewModel = homeViewModel,
- pagingItems = pagingItems,
)
}
animatedComposable(route = "${RouteName.READING}/{articleId}") {
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
index e787100..219ab62 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
@@ -3,7 +3,6 @@ package me.ash.reader.ui.page.home
import androidx.lifecycle.ViewModel
import androidx.paging.*
import androidx.work.WorkManager
-import com.google.accompanist.pager.ExperimentalPagerApi
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
@@ -17,7 +16,6 @@ import me.ash.reader.data.repository.SyncWorker
import me.ash.reader.ui.page.home.flow.FlowItemView
import javax.inject.Inject
-@OptIn(ExperimentalPagerApi::class)
@HiltViewModel
class HomeViewModel @Inject constructor(
private val rssRepository: RssRepository,
@@ -112,7 +110,6 @@ data class FilterState(
val filter: Filter = Filter.All,
)
-@OptIn(ExperimentalPagerApi::class)
data class HomeViewState(
val pagingData: Flow> = emptyFlow(),
val searchContent: String = "",
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt
index 497e251..752b64f 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt
@@ -15,13 +15,16 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import coil.size.Scale
import me.ash.reader.R
import me.ash.reader.data.entity.ArticleWithFeed
import me.ash.reader.data.preference.*
+import me.ash.reader.ui.component.AsyncImage
import me.ash.reader.ui.ext.formatAsString
@Composable
@@ -40,11 +43,12 @@ fun ArticleItem(
Column(
modifier = Modifier
.padding(horizontal = 12.dp)
- .clip(RoundedCornerShape(12.dp))
+ .clip(RoundedCornerShape(20.dp))
.clickable { onClick(articleWithFeed) }
.padding(horizontal = 12.dp, vertical = 12.dp)
.alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f),
) {
+ // Upper
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
@@ -64,6 +68,7 @@ fun ArticleItem(
)
}
+ // Right
if (articleListDate.value) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -95,6 +100,8 @@ fun ArticleItem(
}
}
}
+
+ // Lower
Row(
modifier = Modifier.fillMaxWidth(),
) {
@@ -108,10 +115,12 @@ fun ArticleItem(
) {}
Spacer(modifier = Modifier.width(10.dp))
}
+
// Article
Column(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier.weight(1f),
) {
+
// Title
Text(
text = articleWithFeed.article.title,
@@ -120,6 +129,7 @@ fun ArticleItem(
maxLines = if (articleListDesc.value) 2 else 4,
overflow = TextOverflow.Ellipsis,
)
+
// Description
if (articleListDesc.value && articleWithFeed.article.shortDescription.isNotBlank()) {
Text(
@@ -131,6 +141,19 @@ fun ArticleItem(
)
}
}
+
+ // Image
+ if (articleWithFeed.article.img != null && articleListImage.value) {
+ AsyncImage(
+ modifier = Modifier
+ .padding(start = 10.dp)
+ .size(80.dp)
+ .clip(RoundedCornerShape(20.dp)),
+ data = articleWithFeed.article.img,
+ scale = Scale.FILL,
+ contentScale = ContentScale.Crop,
+ )
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
index c69b0dd..d6a1abd 100644
--- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
@@ -2,7 +2,6 @@ package me.ash.reader.ui.page.home.flow
import androidx.activity.compose.BackHandler
import androidx.compose.animation.*
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@@ -22,7 +21,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.LoadState
-import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.ash.reader.R
@@ -43,7 +42,6 @@ import me.ash.reader.ui.theme.palette.onDark
@OptIn(
ExperimentalMaterial3Api::class,
- ExperimentalFoundationApi::class,
com.google.accompanist.pager.ExperimentalPagerApi::class,
androidx.compose.ui.ExperimentalComposeUiApi::class,
)
@@ -52,8 +50,9 @@ fun FlowPage(
navController: NavHostController,
flowViewModel: FlowViewModel = hiltViewModel(),
homeViewModel: HomeViewModel,
- pagingItems: LazyPagingItems,
) {
+ val homeViewView = homeViewModel.viewState.collectAsStateValue()
+ val pagingItems = homeViewView.pagingData.collectAsLazyPagingItems()
val keyboardController = LocalSoftwareKeyboardController.current
val topBarTonalElevation = LocalFlowTopBarTonalElevation.current
val articleListTonalElevation = LocalFlowArticleListTonalElevation.current
@@ -177,14 +176,6 @@ fun FlowPage(
)
},
content = {
-// if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount == 0) {
-// LottieAnimation(
-// modifier = Modifier
-// .alpha(0.7f)
-// .padding(80.dp),
-// url = "https://assets7.lottiefiles.com/packages/lf20_l4ny0jjm.json",
-// )
-// }
SwipeRefresh(
onRefresh = {
if (!isSyncing) {
diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
index 8c35bd2..a07e097 100644
--- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
+++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt
@@ -174,10 +174,13 @@ fun FlowPageStyle(
}
SettingItem(
title = stringResource(R.string.article_images),
- enable = false,
- onClick = {},
+ onClick = {
+ (!articleListImage).put(context, scope)
+ },
) {
- Switch(activated = false, enable = false)
+ Switch(activated = articleListImage.value) {
+ (!articleListImage).put(context, scope)
+ }
}
SettingItem(
title = stringResource(R.string.article_desc),
@@ -403,6 +406,7 @@ fun FlowPagePreview(
accountId = 0,
date = Date(),
isStarred = true,
+ img = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60"
),
feed = Feed(
id = "",
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
deleted file mode 100644
index 2c950a5..0000000
--- a/app/src/main/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
\ No newline at end of file