Implement pagination for History and Logs

- Integrate Jetpack Paging 3 and Room Paging dependencies.
- Update `LogDao` and `HistoryDao` to return `PagingSource` instead of `Flow<List>`.
- Refactor `ActivitiesScreen`, `ActivityScreen`, `LogsScreen`, and `RelayLogScreen` to use `Pager` and `collectAsLazyPagingItems`.
- Update `LogDatabase` to version 2, adding an index on the `time` column for query performance.
- Extract `ActivityRow` composable for reuse.
- Remove explicit `VACUUM` commands from database setup.
This commit is contained in:
greenart7c3
2025-12-12 07:39:52 -03:00
parent ed64bdbc10
commit 7ad81d1ac3
15 changed files with 366 additions and 215 deletions
+5
View File
@@ -205,6 +205,9 @@ dependencies {
implementation libs.work.runtime.ktx
implementation libs.material3.window
implementation libs.adaptive.android
implementation libs.paging.common
implementation libs.paging.compose
implementation libs.paging.runtime
testImplementation libs.junit
androidTestImplementation libs.ext.junit
@@ -234,6 +237,8 @@ dependencies {
ksp(libs.room.compiler)
implementation(libs.room.ktx)
implementation(libs.room.runtime)
implementation(libs.room.paging)
freeImplementation libs.okhttp
freeImplementation libs.okhttpCoroutines
@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "3d70a45c6ee30a3f8c9d79b6d3a55cf0",
"identityHash": "22c4d1bdcd7396ef577313c1cf4627d7",
"entities": [
{
"tableName": "amber_log",
@@ -44,12 +44,25 @@
"columnNames": [
"id"
]
}
},
"indices": [
{
"name": "log_by_time_key",
"unique": false,
"columnNames": [
"time"
],
"orders": [
"DESC"
],
"createSql": "CREATE INDEX IF NOT EXISTS `log_by_time_key` ON `${TABLE_NAME}` (`time` DESC)"
}
]
}
],
"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, '3d70a45c6ee30a3f8c9d79b6d3a55cf0')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '22c4d1bdcd7396ef577313c1cf4627d7')"
]
}
}
@@ -0,0 +1,68 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "22c4d1bdcd7396ef577313c1cf4627d7",
"entities": [
{
"tableName": "amber_log",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `type` TEXT NOT NULL, `message` TEXT NOT NULL, `time` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "message",
"columnName": "message",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "log_by_time_key",
"unique": false,
"columnNames": [
"time"
],
"orders": [
"DESC"
],
"createSql": "CREATE INDEX IF NOT EXISTS `log_by_time_key` ON `${TABLE_NAME}` (`time` DESC)"
}
]
}
],
"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, '22c4d1bdcd7396ef577313c1cf4627d7')"
]
}
}
@@ -191,7 +191,6 @@ abstract class AppDatabase : RoomDatabase() {
.addMigrations(MIGRATION_14_15)
.addMigrations(MIGRATION_15_16)
.build()
instance.openHelper.writableDatabase.execSQL("VACUUM")
instance
}
@@ -3,6 +3,7 @@ package com.greenart7c3.nostrsigner.database
import android.util.Log
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@@ -11,21 +12,27 @@ import androidx.room.Query
import androidx.room.Transaction
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.models.Permission
import kotlinx.coroutines.flow.Flow
@Dao
interface HistoryDao {
@Query("SELECT * FROM history where pkKey = :pk ORDER BY time DESC")
fun getAllHistory(pk: String): Flow<List<HistoryEntity>>
fun getAllHistoryPaging(pk: String): PagingSource<Int, HistoryEntity>
@Query("SELECT * FROM history where (kind = :query OR LOWER(type) LIKE '%' || :query || '%' OR LOWER(translatedPermission) LIKE '%' || :query || '%') AND pkKey = :pk ORDER BY time DESC")
fun searchAllHistory(pk: String, query: String): Flow<List<HistoryEntity>>
fun searchAllHistoryPaging(pk: String, query: String): PagingSource<Int, HistoryEntity>
@Query("SELECT * FROM history ORDER BY time DESC")
fun getAllHistory(): Flow<List<HistoryEntity>>
fun getAllHistoryPaging(): PagingSource<Int, HistoryEntity>
@Query("SELECT * FROM history where (kind = :query OR LOWER(type) LIKE '%' || :query || '%' OR LOWER(translatedPermission) LIKE '%' || :query || '%') ORDER BY time DESC")
fun searchAllHistory(query: String): Flow<List<HistoryEntity>>
@Query(
"""
SELECT * FROM history
WHERE (kind = :query OR LOWER(type) LIKE '%' || :query || '%'
OR LOWER(translatedPermission) LIKE '%' || :query || '%')
ORDER BY time DESC
""",
)
fun searchAllHistoryPaging(query: String): PagingSource<Int, HistoryEntity>
@Query("DELETE FROM history where pkKey = :pk")
suspend fun deleteHistory(pk: String)
@@ -43,7 +43,6 @@ abstract class HistoryDatabase : RoomDatabase() {
.setTransactionExecutor(transactionExecutor)
.addMigrations(migration_1_2)
.build()
instance.openHelper.writableDatabase.execSQL("VACUUM")
instance
}
@@ -1,5 +1,6 @@
package com.greenart7c3.nostrsigner.database
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@@ -9,6 +10,9 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface LogDao {
@Insert
fun insertAll(items: List<LogEntity>)
@Insert
@Transaction
suspend fun insertLog(logEntity: LogEntity)
@@ -16,9 +20,15 @@ interface LogDao {
@Query("SELECT * FROM amber_log ORDER BY time DESC")
fun getLogs(): Flow<List<LogEntity>>
@Query("SELECT * FROM amber_log ORDER BY time DESC")
fun getLogsPaging(): PagingSource<Int, LogEntity>
@Query("SELECT * FROM amber_log where url = :url ORDER BY time DESC")
fun getLogsByUrl(url: String): Flow<List<LogEntity>>
@Query("SELECT * FROM amber_log where url = :url ORDER BY time DESC")
fun getLogsByUrlPaging(url: String): PagingSource<Int, LogEntity>
@Query("DELETE FROM amber_log")
@Transaction
suspend fun clearLogs()
@@ -5,19 +5,27 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import java.util.concurrent.Executors
@Database(
entities = [
LogEntity::class,
],
version = 1,
version = 2,
)
@TypeConverters(Converters::class)
abstract class LogDatabase : RoomDatabase() {
abstract fun dao(): LogDao
companion object {
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE INDEX IF NOT EXISTS `log_by_time_key` ON `amber_log` (`time` DESC)")
}
}
fun getDatabase(
context: Context,
npub: String,
@@ -33,8 +41,8 @@ abstract class LogDatabase : RoomDatabase() {
)
.setQueryExecutor(executor)
.setTransactionExecutor(transactionExecutor)
.addMigrations(migration_1_2)
.build()
instance.openHelper.writableDatabase.execSQL("VACUUM")
instance
}
@@ -1,10 +1,19 @@
package com.greenart7c3.nostrsigner.database
import androidx.room.Entity
import androidx.room.Index
import androidx.room.Index.Order
import androidx.room.PrimaryKey
@Entity(
tableName = "amber_log",
indices = [
Index(
value = ["time"],
name = "log_by_time_key",
orders = [Order.DESC],
),
],
)
data class LogEntity(
@PrimaryKey(autoGenerate = true)
@@ -1,5 +1,6 @@
package com.greenart7c3.nostrsigner.ui
import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -9,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
@@ -31,18 +31,20 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.database.AppDatabase
import com.greenart7c3.nostrsigner.database.HistoryEntity
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.TimeUtils
import com.greenart7c3.nostrsigner.models.supportedKindNumbers
@@ -63,10 +65,24 @@ fun ActivitiesScreen(
val database = Amber.instance.getHistoryDatabase(account.npub)
val context = LocalContext.current
// State for the search query
var searchQuery by remember { mutableStateOf("") }
val filteredActivities = if (searchQuery.isEmpty()) database.dao().getAllHistory().collectAsStateWithLifecycle(emptyList()) else database.dao().searchAllHistory(searchQuery.toLowerCase(Locale.current)).collectAsStateWithLifecycle(emptyList())
val pager = remember(searchQuery) {
Pager(
PagingConfig(
pageSize = 20,
enablePlaceholders = false,
),
) {
if (searchQuery.isEmpty()) {
database.dao().getAllHistoryPaging()
} else {
database.dao().searchAllHistoryPaging(searchQuery.lowercase())
}
}
}
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
val textFieldState by remember { mutableStateOf(TextFieldState(initialText = searchQuery)) }
@@ -92,7 +108,7 @@ fun ActivitiesScreen(
),
) {
item {
if (filteredActivities.value.isEmpty()) {
if (lazyPagingItems.itemCount == 0) {
Text(
stringResource(R.string.no_activities_found),
Modifier
@@ -105,58 +121,78 @@ fun ActivitiesScreen(
}
}
items(filteredActivities.value) { activity ->
Column {
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier.fillMaxWidth(0.9f),
verticalArrangement = Arrangement.Center,
) {
ApplicationName(
key = activity.pkKey,
accepted = activity.accepted,
database = Amber.instance.getDatabase(account.npub),
account = account,
)
items(lazyPagingItems.itemCount) { index ->
val activity = lazyPagingItems[index]
if (activity != null) {
ActivityRow(activity = activity, account = account)
}
}
Text(
text = activity.translatedPermission,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (activity.accepted) Color.Unspecified else Color.Gray,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(activity.time * 1000),
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = Color.Gray,
)
}
Icon(
if (activity.accepted) Icons.Default.Check else Icons.Default.Close,
contentDescription = if (activity.accepted) stringResource(R.string.accepted) else stringResource(R.string.rejected),
tint = if (activity.accepted) Color(0xFF1D8802) else Color(0xFFFF6B00),
modifier = Modifier.padding(start = 10.dp, top = 4.dp, bottom = 16.dp),
)
lazyPagingItems.apply {
when (loadState.refresh) {
is LoadState.Loading -> item {
Log.d("ActivitiesScreen", "Loading...")
Text("Loading...", Modifier.padding(16.dp))
}
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
is LoadState.Error -> item {
Log.d("ActivitiesScreen", "Error loading data")
Text("Error loading data", Modifier.padding(16.dp))
}
is LoadState.NotLoading -> { }
}
}
}
}
}
@Composable
fun ActivityRow(activity: HistoryEntity, account: Account) {
Column {
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier.fillMaxWidth(0.9f),
verticalArrangement = Arrangement.Center,
) {
ApplicationName(
key = activity.pkKey,
accepted = activity.accepted,
database = Amber.instance.getDatabase(account.npub),
account = account,
)
Text(
text = activity.translatedPermission,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (activity.accepted) Color.Unspecified else Color.Gray,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(activity.time * 1000),
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = Color.Gray,
)
}
Icon(
if (activity.accepted) Icons.Default.Check else Icons.Default.Close,
contentDescription = null,
tint = if (activity.accepted) Color(0xFF1D8802) else Color(0xFFFF6B00),
modifier = Modifier.padding(start = 10.dp, top = 4.dp, bottom = 16.dp),
)
}
Spacer(Modifier.weight(1f))
HorizontalDivider(color = MaterialTheme.colorScheme.primary)
}
}
@Composable
fun ApplicationName(
key: String,
@@ -21,7 +21,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
@@ -36,8 +38,17 @@ fun LogsScreen(
account: Account,
) {
val scope = rememberCoroutineScope()
val logsFlow = Amber.instance.getLogDatabase(account.npub).dao().getLogs()
val logs = logsFlow.collectAsStateWithLifecycle(initialValue = emptyList())
val pager =
Pager(
PagingConfig(
pageSize = 20,
enablePlaceholders = false,
),
) {
Amber.instance.getLogDatabase(account.npub).dao().getLogsPaging()
}
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
val state = rememberLazyListState()
LazyColumn(
@@ -57,45 +68,48 @@ fun LogsScreen(
text = stringResource(R.string.clear_logs),
)
}
items(logs.value) { log ->
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
verticalArrangement = Arrangement.Center,
items(lazyPagingItems.itemCount) { index ->
val log = lazyPagingItems[index]
log?.let {
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(log.time),
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.url,
fontSize = 20.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.type,
fontSize = 20.sp,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = log.message,
fontSize = 20.sp,
)
Column(
verticalArrangement = Arrangement.Center,
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(log.time),
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.url,
fontSize = 20.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.type,
fontSize = 20.sp,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = log.message,
fontSize = 20.sp,
)
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
}
}
}
}
@@ -696,6 +696,7 @@ fun MainScreen(
val localUrl = Base64.getDecoder().decode(url).toString(Charsets.UTF_8)
RelayLogScreen(
url = localUrl,
account = account,
paddingValues = PaddingValues(
top = padding.calculateTopPadding() + (verticalPadding * 1.5f),
bottom = padding.calculateBottomPadding(),
@@ -1,49 +1,34 @@
package com.greenart7c3.nostrsigner.ui.actions
import androidx.compose.foundation.layout.Arrangement
import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.TimeUtils
import com.greenart7c3.nostrsigner.models.supportedKindNumbers
import com.greenart7c3.nostrsigner.ui.ActivityRow
import com.greenart7c3.nostrsigner.ui.components.SimpleSearchBar
@OptIn(ExperimentalMaterial3Api::class)
@@ -55,9 +40,28 @@ fun ActivityScreen(
account: Account,
key: String,
) {
val database = Amber.instance.getHistoryDatabase(account.npub)
val context = LocalContext.current
var searchQuery by remember { mutableStateOf("") }
val filteredActivities = if (searchQuery.isEmpty()) Amber.instance.getHistoryDatabase(account.npub).dao().getAllHistory(key).collectAsStateWithLifecycle(emptyList()) else Amber.instance.getHistoryDatabase(account.npub).dao().searchAllHistory(key, searchQuery.toLowerCase(Locale.current)).collectAsStateWithLifecycle(emptyList())
val pager = remember(searchQuery) {
Pager(
PagingConfig(
pageSize = 20,
enablePlaceholders = false,
),
) {
if (searchQuery.isEmpty()) {
database.dao().getAllHistoryPaging()
} else {
database.dao().searchAllHistoryPaging(searchQuery.lowercase())
}
}
}
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
val textFieldState by remember { mutableStateOf(TextFieldState(initialText = searchQuery)) }
Column(
@@ -81,60 +85,24 @@ fun ActivityScreen(
bottom = paddingValues.calculateBottomPadding(),
),
) {
item {
if (filteredActivities.value.isEmpty()) {
Text(
stringResource(R.string.no_activities_found),
Modifier
.fillMaxWidth()
.padding(8.dp),
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
fontSize = 18.sp,
)
items(lazyPagingItems.itemCount) { index ->
val activity = lazyPagingItems[index]
activity?.let {
ActivityRow(activity = activity, account = account)
}
}
items(filteredActivities.value) { activity ->
Column {
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier.fillMaxWidth(0.9f),
verticalArrangement = Arrangement.Center,
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = activity.translatedPermission,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (activity.accepted) Color.Unspecified else Color.Gray,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(activity.time * 1000),
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = Color.Gray,
)
}
Icon(
if (activity.accepted) Icons.Default.Check else Icons.Default.Close,
contentDescription = if (activity.accepted) stringResource(R.string.accepted) else stringResource(R.string.rejected),
tint = if (activity.accepted) Color(0xFF1D8802) else Color(0xFFFF6B00),
modifier = Modifier.padding(start = 10.dp, top = 4.dp, bottom = 16.dp),
)
lazyPagingItems.apply {
when (loadState.refresh) {
is LoadState.Loading -> item {
Log.d("ActivitiesScreen", "Loading...")
Text("Loading...", Modifier.padding(16.dp))
}
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
is LoadState.Error -> item {
Log.d("ActivitiesScreen", "Error loading data")
Text("Error loading data", Modifier.padding(16.dp))
}
is LoadState.NotLoading -> { }
}
}
}
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider
@@ -43,6 +42,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.BuildConfig
import com.greenart7c3.nostrsigner.LocalPreferences
@@ -76,7 +78,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -438,52 +439,59 @@ fun onAddRelay(
@Composable
fun RelayLogScreen(
paddingValues: PaddingValues,
account: Account,
url: String,
) {
val context = LocalContext.current
val flows = LocalPreferences.allSavedAccounts(context).map {
Amber.instance.getLogDatabase(it.npub).dao().getLogsByUrl(url)
}.merge()
val logs = flows.collectAsStateWithLifecycle(initialValue = emptyList())
val pager =
Pager(
PagingConfig(
pageSize = 20,
enablePlaceholders = false,
),
) {
Amber.instance.getLogDatabase(account.npub).dao().getLogsByUrlPaging(url)
}
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
LazyColumn(
Modifier.fillMaxSize(),
contentPadding = paddingValues,
) {
itemsIndexed(logs.value) { _, log ->
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
verticalArrangement = Arrangement.Center,
items(lazyPagingItems.itemCount) { index ->
val log = lazyPagingItems[index]
log?.let {
Row(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = formatLongToCustomDateTimeWithSeconds(log.time),
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.type,
fontSize = 20.sp,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = log.message,
fontSize = 20.sp,
)
Column(
verticalArrangement = Arrangement.Center,
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = formatLongToCustomDateTimeWithSeconds(log.time),
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = log.type,
fontSize = 20.sp,
)
Text(
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
text = log.message,
fontSize = 20.sp,
)
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.weight(1f))
HorizontalDivider(
color = MaterialTheme.colorScheme.primary,
)
}
}
}
}
+6
View File
@@ -30,6 +30,7 @@ jna = "5.18.1"
adaptiveAndroid = "1.2.0"
materialIconsExtended = "1.7.8"
collections = "0.4.0"
pagingCommon = "3.3.6"
[libraries]
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
@@ -57,6 +58,8 @@ richtext-ui = { module = "com.github.vitorpamplona.compose-richtext:richtext-ui"
richtext-ui-material3 = { module = "com.github.vitorpamplona.compose-richtext:richtext-ui-material3", version.ref = "richtextUi" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomKtx" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomKtx" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomKtx" }
room-paging = { module = "androidx.room:room-paging", version.ref = "roomKtx" }
runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata", version.ref = "compose_ui" }
security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCryptoKtx" }
security-crypto-ktx = { module = "androidx.security:security-crypto-ktx", version.ref = "securityCryptoKtx" }
@@ -76,6 +79,9 @@ coil-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", versio
storage = { module = "com.anggrayudi:storage", version.ref = "storage" }
material3-window = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3" }
adaptive-android = { group = "androidx.compose.material3.adaptive", name = "adaptive-android", version.ref = "adaptiveAndroid" }
paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "pagingCommon" }
paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "pagingCommon" }
paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "pagingCommon" }
[plugins]
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }