diff --git a/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json index e8ac33e6..5da3ddd0 100644 --- a/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json +++ b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "29d5da9510c1dd78f8bdf4641d6cdf33", + "identityHash": "ffb6b79cf1c4bf0c70f58083cfc0990e", "entities": [ { "tableName": "history", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `time` INTEGER NOT NULL, `accepted` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `time` INTEGER NOT NULL, `accepted` INTEGER NOT NULL, `translatedPermission` TEXT NOT NULL)", "fields": [ { "fieldPath": "id", @@ -42,6 +42,12 @@ "columnName": "accepted", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "translatedPermission", + "columnName": "translatedPermission", + "affinity": "TEXT", + "notNull": true } ], "primaryKey": { @@ -93,7 +99,7 @@ ], "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, '29d5da9510c1dd78f8bdf4641d6cdf33')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ffb6b79cf1c4bf0c70f58083cfc0990e')" ] } } \ No newline at end of file diff --git a/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/2.json b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/2.json new file mode 100644 index 00000000..21e2b1f5 --- /dev/null +++ b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/2.json @@ -0,0 +1,105 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "ffb6b79cf1c4bf0c70f58083cfc0990e", + "entities": [ + { + "tableName": "history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `time` INTEGER NOT NULL, `accepted` INTEGER NOT NULL, `translatedPermission` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pkKey", + "columnName": "pkKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accepted", + "columnName": "accepted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "translatedPermission", + "columnName": "translatedPermission", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "history_by_pk_key", + "unique": false, + "columnNames": [ + "pkKey" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_pk_key` ON `${TABLE_NAME}` (`pkKey`)" + }, + { + "name": "history_by_id", + "unique": false, + "columnNames": [ + "id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "history_by_time", + "unique": false, + "columnNames": [ + "time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_time` ON `${TABLE_NAME}` (`time`)" + }, + { + "name": "history_by_key_and_time", + "unique": false, + "columnNames": [ + "pkKey", + "time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_key_and_time` ON `${TABLE_NAME}` (`pkKey`, `time`)" + } + ] + } + ], + "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, 'ffb6b79cf1c4bf0c70f58083cfc0990e')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt index 6cfeb65f..108547c8 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt @@ -1,6 +1,8 @@ package com.greenart7c3.nostrsigner.database import android.util.Log +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toLowerCase import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -8,6 +10,7 @@ import androidx.room.OnConflictStrategy 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 @@ -15,9 +18,15 @@ interface HistoryDao { @Query("SELECT * FROM history where pkKey = :pk ORDER BY time DESC") fun getAllHistory(pk: String): Flow> + @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> + @Query("SELECT * FROM history ORDER BY time DESC") fun getAllHistory(): Flow> + @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> + @Query("DELETE FROM history where pkKey = :pk") suspend fun deleteHistory(pk: String) @@ -41,7 +50,11 @@ interface HistoryDao { @Transaction suspend fun addHistory(entity: HistoryEntity, npub: String?) { try { - innerAddHistory(entity) + val permission = Permission(entity.type.toLowerCase(Locale.current), entity.kind) + val localEntity = entity.copy( + translatedPermission = permission.toLocalizedString(Amber.instance, true), + ) + innerAddHistory(localEntity) npub?.let { Amber.instance.getDatabase(npub).dao().updateLastUsed(entity.pkKey, entity.time) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt index 7d5d400f..42e7c908 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt @@ -5,13 +5,21 @@ 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 +val migration_1_2 = object : Migration(1, 2) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE history ADD COLUMN translatedPermission TEXT NOT NULL DEFAULT ''") + } +} + @Database( entities = [ HistoryEntity::class, ], - version = 1, + version = 2, ) @TypeConverters(Converters::class) abstract class HistoryDatabase : RoomDatabase() { @@ -33,6 +41,7 @@ abstract class HistoryDatabase : RoomDatabase() { ) .setQueryExecutor(executor) .setTransactionExecutor(transactionExecutor) + .addMigrations(migration_1_2) .build() instance.openHelper.writableDatabase.execSQL("VACUUM") diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt index 87937e6d..40337fa2 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt @@ -33,4 +33,5 @@ data class HistoryEntity( val kind: Int?, val time: Long, val accepted: Boolean, + val translatedPermission: String = "", ) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt index 1313323b..c6c9e2cf 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt @@ -44,7 +44,6 @@ import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.database.AppDatabase import com.greenart7c3.nostrsigner.models.Account -import com.greenart7c3.nostrsigner.models.Permission import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.models.supportedKindNumbers import com.greenart7c3.nostrsigner.service.ApplicationNameCache @@ -62,20 +61,13 @@ fun ActivitiesScreen( account: Account, ) { val database = Amber.instance.getHistoryDatabase(account.npub) - val activities = database.dao().getAllHistory().collectAsStateWithLifecycle(emptyList()) + val context = LocalContext.current // State for the search query var searchQuery by remember { mutableStateOf("") } - // Filtered activities based on the search query - val filteredActivities = activities.value.filter { activity -> - if (searchQuery.isEmpty()) { - true - } else { - val permission = Permission(activity.type.toLowerCase(Locale.current), activity.kind) - permission.toLocalizedString(context, true).contains(searchQuery, ignoreCase = true) - } - } + val filteredActivities = if (searchQuery.isEmpty()) database.dao().getAllHistory().collectAsStateWithLifecycle(emptyList()) else database.dao().searchAllHistory(searchQuery.toLowerCase(Locale.current)).collectAsStateWithLifecycle(emptyList()) + val textFieldState by remember { mutableStateOf(TextFieldState(initialText = searchQuery)) } Column( @@ -100,7 +92,7 @@ fun ActivitiesScreen( ), ) { item { - if (filteredActivities.isEmpty()) { + if (filteredActivities.value.isEmpty()) { Text( stringResource(R.string.no_activities_found), Modifier @@ -113,12 +105,7 @@ fun ActivitiesScreen( } } - items(filteredActivities) { activity -> - val permission = - Permission( - activity.type.toLowerCase(Locale.current), - activity.kind, - ) + items(filteredActivities.value) { activity -> Column { Row( modifier = Modifier @@ -138,7 +125,7 @@ fun ActivitiesScreen( ) Text( - text = if (permission.type == "connect") stringResource(R.string.connect) else permission.toLocalizedString(context), + text = activity.translatedPermission, maxLines = 1, overflow = TextOverflow.Ellipsis, color = if (activity.accepted) Color.Unspecified else Color.Gray, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt index 0b1b1cdf..a33339d2 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt @@ -42,7 +42,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.models.Account -import com.greenart7c3.nostrsigner.models.Permission import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.models.supportedKindNumbers import com.greenart7c3.nostrsigner.ui.components.SimpleSearchBar @@ -56,20 +55,9 @@ fun ActivityScreen( account: Account, key: String, ) { - val activities = Amber.instance.getHistoryDatabase(account.npub).dao().getAllHistory(key).collectAsStateWithLifecycle(emptyList()) val context = LocalContext.current - // State for the search query var searchQuery by remember { mutableStateOf("") } - - // Filtered activities based on the search query - val filteredActivities = activities.value.filter { activity -> - if (searchQuery.isEmpty()) { - true - } else { - val permission = Permission(activity.type.toLowerCase(Locale.current), activity.kind) - permission.toLocalizedString(context, true).contains(searchQuery, ignoreCase = true) - } - } + 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 textFieldState by remember { mutableStateOf(TextFieldState(initialText = searchQuery)) } Column( @@ -94,7 +82,7 @@ fun ActivityScreen( ), ) { item { - if (filteredActivities.isEmpty()) { + if (filteredActivities.value.isEmpty()) { Text( stringResource(R.string.no_activities_found), Modifier @@ -107,12 +95,7 @@ fun ActivityScreen( } } - items(filteredActivities) { activity -> - val permission = - Permission( - activity.type.toLowerCase(Locale.current), - activity.kind, - ) + items(filteredActivities.value) { activity -> Column { Row( modifier = Modifier @@ -126,7 +109,7 @@ fun ActivityScreen( ) { Text( modifier = Modifier.padding(top = 16.dp), - text = if (permission.type == "connect") stringResource(R.string.connect) else permission.toLocalizedString(context), + text = activity.translatedPermission, maxLines = 1, overflow = TextOverflow.Ellipsis, color = if (activity.accepted) Color.Unspecified else Color.Gray,