Show local profiles on search
This commit is contained in:
@@ -3,14 +3,13 @@ package ac.aux.compose.database.dao
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
|
||||
@Dao
|
||||
interface ProfileDao {
|
||||
@Query("SELECT * FROM Profile")
|
||||
fun getAllPosts(): List<Profile>
|
||||
@Query("SELECT * FROM Profile LIMIT :limit")
|
||||
fun getAllProfiles(limit: Int = 21): List<Profile>
|
||||
|
||||
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
||||
fun getProfileByPublicKey(publicKey: String): Profile?
|
||||
|
||||
@@ -320,4 +320,12 @@ class DatabaseNostrRepository(
|
||||
return database.nostrEventDao().observeNostrEventById(nostrEventId)
|
||||
}
|
||||
|
||||
override suspend fun searchableProfiles(): List<Profile> {
|
||||
return database.profileDao().getAllProfiles()
|
||||
}
|
||||
|
||||
override suspend fun recentSearches(): List<String> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ac.aux.compose.database.repository
|
||||
|
||||
import ac.aux.compose.database.AuxDatabase
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
||||
import ac.aux.compose.repository.SearchRepository
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ interface NostrRepository {
|
||||
|
||||
suspend fun observeLocalNostrEventById(nostrEventId: String): Flow<LocalNostrEvent?>
|
||||
|
||||
suspend fun searchableProfiles(): List<Profile>
|
||||
|
||||
suspend fun recentSearches(): List<String>
|
||||
|
||||
|
||||
companion object {
|
||||
val NO_OP_NOSTR_REPOSITORY = object : NostrRepository {
|
||||
override suspend fun observeProfile(publicKey: HexKey): Flow<LocalProfile?> {
|
||||
@@ -158,6 +163,14 @@ interface NostrRepository {
|
||||
override suspend fun observeLocalNostrEventById(nostrEventId: String): Flow<LocalNostrEvent?> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun searchableProfiles(): List<Profile> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun recentSearches(): List<String> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package ac.aux.compose.repository
|
||||
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||
|
||||
interface SearchRepository {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.aux.compose.ui.composable
|
||||
import ac.aux.compose.repository.NostrRepository
|
||||
import ac.aux.compose.repository.SearchRepository
|
||||
import ac.aux.compose.ui.composable.navigation.routes.ImplementationPendingRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.Route
|
||||
import ac.aux.compose.ui.composable.navigation.routes.SearchResultRoute
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
@@ -18,6 +19,7 @@ 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.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
@@ -43,8 +45,10 @@ import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExpandedFullScreenSearchBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -72,11 +76,12 @@ import androidx.compose.ui.semantics.isTraversalGroup
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.traversalIndex
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SearchScreen(
|
||||
initialSearchUIState: SearchUIState,
|
||||
@@ -182,7 +187,7 @@ fun SearchScreen(
|
||||
)
|
||||
val textFieldState = rememberTextFieldState()
|
||||
// Controls expansion state of the search bar
|
||||
var expanded by rememberSaveable { mutableStateOf(true) }
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
@@ -359,50 +364,69 @@ fun SearchScreen(
|
||||
}
|
||||
} else {
|
||||
|
||||
// Display search results in a scrollable column
|
||||
|
||||
|
||||
|
||||
LazyColumn {
|
||||
// Display search results in a scrollable column
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp),
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = "Profiles",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
item {
|
||||
LazyRow {
|
||||
items(profileSearches) { profile ->
|
||||
searchViewModel.searchableProfiles.let { searchableProfiles ->
|
||||
if (searchableProfiles.isNotEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
// TODO: Open profile...
|
||||
onNavigateToProfile.invoke(
|
||||
ImplementationPendingRoute("Profile search result")
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Person,
|
||||
contentDescription = profile
|
||||
Text(
|
||||
text = "Profiles",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = profile,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
LazyRow {
|
||||
items(searchViewModel.searchableProfiles) { profile ->
|
||||
val profileName = profile.displayName ?: profile.userName ?: profile.nip05 ?: profile.nostrEventId
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp).width(70.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier
|
||||
.size(IconButtonDefaults.mediumContainerSize()),
|
||||
onClick = {
|
||||
onNavigateToProfile.invoke(
|
||||
NostrEventDetailRoute(
|
||||
profile.nostrEventId
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(70.dp),
|
||||
imageVector = Icons.Default.Person,
|
||||
contentDescription = profileName
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = profileName,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
item {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package ac.aux.compose.ui.view.model
|
||||
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||
import ac.aux.compose.database.model.types.SynchronizationFilter
|
||||
import ac.aux.compose.managers.SeedManager
|
||||
import ac.aux.compose.nostr.Relays
|
||||
@@ -9,6 +11,7 @@ import ac.aux.compose.repository.SearchRepository
|
||||
import ac.aux.compose.ui.view.state.SearchUIState
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
@@ -36,7 +39,12 @@ class SearchViewModel(
|
||||
var searchUIState: SearchUIState by mutableStateOf(initialSearchUIState)
|
||||
private set
|
||||
|
||||
var searchableProfiles = mutableStateListOf<Profile>()
|
||||
private set
|
||||
|
||||
init {
|
||||
loadSearchableProfiles()
|
||||
}
|
||||
fun performSearch(searchQuery: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// Sync Notifications... might want to also run this in the background
|
||||
@@ -63,6 +71,13 @@ class SearchViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSearchableProfiles() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
searchableProfiles.addAll(
|
||||
nostrRepository.searchableProfiles()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "SearchViewModel"
|
||||
|
||||
Reference in New Issue
Block a user