Moves global to home as a list and search to the top bar.
This commit is contained in:
@@ -14,7 +14,6 @@ import com.vitorpamplona.amethyst.service.HttpClient
|
|||||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
||||||
@@ -89,7 +88,6 @@ object ServiceManager {
|
|||||||
NostrChannelDataSource.stop()
|
NostrChannelDataSource.stop()
|
||||||
NostrChatroomListDataSource.stop()
|
NostrChatroomListDataSource.stop()
|
||||||
|
|
||||||
NostrGlobalDataSource.stop()
|
|
||||||
NostrSingleChannelDataSource.stop()
|
NostrSingleChannelDataSource.stop()
|
||||||
NostrSingleEventDataSource.stop()
|
NostrSingleEventDataSource.stop()
|
||||||
NostrSingleUserDataSource.stop()
|
NostrSingleUserDataSource.stop()
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.vitorpamplona.amethyst.service
|
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.HighlightEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.PinListEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.PollNoteEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
|
||||||
|
|
||||||
object NostrGlobalDataSource : NostrDataSource("GlobalFeed") {
|
|
||||||
fun createGlobalFilter() = TypedFilter(
|
|
||||||
types = setOf(FeedType.GLOBAL),
|
|
||||||
filter = JsonFilter(
|
|
||||||
kinds = listOf(TextNoteEvent.kind, PollNoteEvent.kind, ChannelMessageEvent.kind, AudioTrackEvent.kind, PinListEvent.kind, LongTextNoteEvent.kind, HighlightEvent.kind),
|
|
||||||
limit = 200
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val globalFeedChannel = requestNewChannel()
|
|
||||||
|
|
||||||
override fun updateChannelFilters() {
|
|
||||||
globalFeedChannel.typedFilters = listOf(createGlobalFilter()).ifEmpty { null }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,13 +51,13 @@ object NostrHomeDataSource : NostrDataSource("HomeFeed") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createFollowAccountsFilter(): TypedFilter {
|
fun createFollowAccountsFilter(): TypedFilter {
|
||||||
val follows = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val follows = account.selectedUsersFollowList(account.defaultHomeFollowList)
|
||||||
|
|
||||||
val followKeys = follows.map {
|
val followKeys = follows?.map {
|
||||||
it.substring(0, 6)
|
it.substring(0, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
val followSet = followKeys.plus(account.userProfile().pubkeyHex.substring(0, 6))
|
val followSet = followKeys?.plus(account.userProfile().pubkeyHex.substring(0, 6))
|
||||||
|
|
||||||
return TypedFilter(
|
return TypedFilter(
|
||||||
types = setOf(FeedType.FOLLOWS),
|
types = setOf(FeedType.FOLLOWS),
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.dal
|
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
|
||||||
import com.vitorpamplona.amethyst.service.model.*
|
|
||||||
|
|
||||||
class GlobalFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
|
||||||
|
|
||||||
override fun feedKey(): String {
|
|
||||||
return account.userProfile().pubkeyHex
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun feed(): List<Note> {
|
|
||||||
val notes = innerApplyFilter(LocalCache.notes.values)
|
|
||||||
val longFormNotes = innerApplyFilter(LocalCache.addressables.values)
|
|
||||||
|
|
||||||
return sort(notes + longFormNotes)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
|
||||||
return innerApplyFilter(collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
|
||||||
val followChannels = account.followingChannels
|
|
||||||
val followUsers = account.followingKeySet()
|
|
||||||
val now = System.currentTimeMillis() / 1000
|
|
||||||
|
|
||||||
return collection
|
|
||||||
.asSequence()
|
|
||||||
.filter {
|
|
||||||
(it.event is BaseTextNoteEvent || it.event is AudioTrackEvent) && it.replyTo.isNullOrEmpty()
|
|
||||||
}
|
|
||||||
.filter {
|
|
||||||
val channel = it.channelHex()
|
|
||||||
// does not show events already in the public chat list
|
|
||||||
(channel == null || channel !in followChannels) &&
|
|
||||||
// does not show people the user already follows
|
|
||||||
(it.author?.pubkeyHex !in followUsers)
|
|
||||||
}
|
|
||||||
.filter { account.isAcceptable(it) }
|
|
||||||
.filter {
|
|
||||||
// Do not show notes with the creation time exceeding the current time, as they will always stay at the top of the global feed, which is cheating.
|
|
||||||
it.createdAt()!! <= now
|
|
||||||
}
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun sort(collection: Set<Note>): List<Note> {
|
|
||||||
return collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.dal
|
package com.vitorpamplona.amethyst.ui.dal
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
||||||
@@ -23,6 +24,8 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
|
val isGlobal = account.defaultHomeFollowList == GLOBAL_FOLLOWS
|
||||||
|
|
||||||
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
|||||||
.asSequence()
|
.asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
(it.event is TextNoteEvent || it.event is PollNoteEvent || it.event is ChannelMessageEvent || it.event is LiveActivitiesChatMessageEvent) &&
|
(it.event is TextNoteEvent || it.event is PollNoteEvent || it.event is ChannelMessageEvent || it.event is LiveActivitiesChatMessageEvent) &&
|
||||||
(it.author?.pubkeyHex in followingKeySet || (it.event?.isTaggedHashes(followingTagSet) ?: false)) &&
|
(isGlobal || it.author?.pubkeyHex in followingKeySet || it.event?.isTaggedHashes(followingTagSet) ?: false) &&
|
||||||
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
||||||
it.author?.let { !account.isHidden(it) } ?: true &&
|
it.author?.let { !account.isHidden(it) } ?: true &&
|
||||||
((it.event?.createdAt() ?: 0) < now) &&
|
((it.event?.createdAt() ?: 0) < now) &&
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.dal
|
package com.vitorpamplona.amethyst.ui.dal
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||||
@@ -29,6 +30,8 @@ class HomeLiveActivitiesFeedFilter(val account: Account) : AdditiveFeedFilter<No
|
|||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
|
val isGlobal = account.defaultHomeFollowList == GLOBAL_FOLLOWS
|
||||||
|
|
||||||
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
|
|
||||||
@@ -39,7 +42,7 @@ class HomeLiveActivitiesFeedFilter(val account: Account) : AdditiveFeedFilter<No
|
|||||||
.filter { it ->
|
.filter { it ->
|
||||||
val noteEvent = it.event
|
val noteEvent = it.event
|
||||||
(noteEvent is LiveActivitiesEvent && noteEvent.createdAt > twoHrs && noteEvent.status() == "live" && OnlineChecker.isOnline(noteEvent.streaming())) &&
|
(noteEvent is LiveActivitiesEvent && noteEvent.createdAt > twoHrs && noteEvent.status() == "live" && OnlineChecker.isOnline(noteEvent.streaming())) &&
|
||||||
(it.author?.pubkeyHex in followingKeySet || (noteEvent.isTaggedHashes(followingTagSet))) &&
|
(isGlobal || it.author?.pubkeyHex in followingKeySet || noteEvent.isTaggedHashes(followingTagSet)) &&
|
||||||
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
||||||
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true
|
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.dal
|
package com.vitorpamplona.amethyst.ui.dal
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
|
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
|
||||||
@@ -29,6 +30,8 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
|
val isGlobal = account.defaultHomeFollowList == GLOBAL_FOLLOWS
|
||||||
|
|
||||||
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingKeySet = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
val followingTagSet = account.selectedTagsFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||||
|
|
||||||
@@ -40,7 +43,7 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
|||||||
.filter { it ->
|
.filter { it ->
|
||||||
val noteEvent = it.event
|
val noteEvent = it.event
|
||||||
(noteEvent is TextNoteEvent || noteEvent is RepostEvent || noteEvent is GenericRepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent) &&
|
(noteEvent is TextNoteEvent || noteEvent is RepostEvent || noteEvent is GenericRepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent) &&
|
||||||
(it.author?.pubkeyHex in followingKeySet || (noteEvent.isTaggedHashes(followingTagSet))) &&
|
(isGlobal || it.author?.pubkeyHex in followingKeySet || noteEvent.isTaggedHashes(followingTagSet)) &&
|
||||||
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
// && account.isAcceptable(it) // This filter follows only. No need to check if acceptable
|
||||||
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
|
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
|
||||||
((it.event?.createdAt() ?: 0) < oneMinuteInTheFuture) &&
|
((it.event?.createdAt() ?: 0) < oneMinuteInTheFuture) &&
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ val bottomNavigationItems = listOf(
|
|||||||
Route.Home,
|
Route.Home,
|
||||||
Route.Message,
|
Route.Message,
|
||||||
Route.Video,
|
Route.Video,
|
||||||
Route.Search,
|
|
||||||
Route.Notification
|
Route.Notification
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import androidx.navigation.compose.composable
|
|||||||
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
|
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedLiveActivitiesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedLiveActivitiesViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
|
||||||
@@ -42,7 +41,6 @@ fun AppNavigation(
|
|||||||
liveActivitiesViewModel: NostrHomeFeedLiveActivitiesViewModel,
|
liveActivitiesViewModel: NostrHomeFeedLiveActivitiesViewModel,
|
||||||
knownFeedViewModel: NostrChatroomListKnownFeedViewModel,
|
knownFeedViewModel: NostrChatroomListKnownFeedViewModel,
|
||||||
newFeedViewModel: NostrChatroomListNewFeedViewModel,
|
newFeedViewModel: NostrChatroomListNewFeedViewModel,
|
||||||
searchFeedViewModel: NostrGlobalFeedViewModel,
|
|
||||||
videoFeedViewModel: NostrVideoFeedViewModel,
|
videoFeedViewModel: NostrVideoFeedViewModel,
|
||||||
notifFeedViewModel: NotificationViewModel,
|
notifFeedViewModel: NotificationViewModel,
|
||||||
userReactionsStatsModel: UserReactionsViewModel,
|
userReactionsStatsModel: UserReactionsViewModel,
|
||||||
@@ -111,7 +109,6 @@ fun AppNavigation(
|
|||||||
Route.Search.let { route ->
|
Route.Search.let { route ->
|
||||||
composable(route.route, route.arguments, content = {
|
composable(route.route, route.arguments, content = {
|
||||||
SearchScreen(
|
SearchScreen(
|
||||||
searchFeedViewModel = searchFeedViewModel,
|
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav
|
nav = nav
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import coil.Coil
|
import coil.Coil
|
||||||
@@ -61,7 +60,6 @@ import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
|||||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
|
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
|
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||||
@@ -74,9 +72,7 @@ import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
|||||||
import com.vitorpamplona.amethyst.service.model.PeopleListEvent
|
import com.vitorpamplona.amethyst.service.model.PeopleListEvent
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
import com.vitorpamplona.amethyst.service.relays.Client
|
||||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||||
import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
|
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||||
@@ -158,7 +154,7 @@ fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, a
|
|||||||
FollowList(
|
FollowList(
|
||||||
followLists,
|
followLists,
|
||||||
list,
|
list,
|
||||||
false
|
true
|
||||||
) { listName ->
|
) { listName ->
|
||||||
accountViewModel.account.changeDefaultHomeFollowList(listName)
|
accountViewModel.account.changeDefaultHomeFollowList(listName)
|
||||||
}
|
}
|
||||||
@@ -194,14 +190,6 @@ fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel,
|
|||||||
fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit, content: @Composable (AccountViewModel) -> Unit) {
|
fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit, content: @Composable (AccountViewModel) -> Unit) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
var wantsToEditRelays by remember {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wantsToEditRelays) {
|
|
||||||
NewRelayListView({ wantsToEditRelays = false }, accountViewModel, nav = nav)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(modifier = Modifier.height(50.dp)) {
|
Column(modifier = Modifier.height(50.dp)) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
elevation = 0.dp,
|
elevation = 0.dp,
|
||||||
@@ -222,10 +210,6 @@ fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewMod
|
|||||||
) {
|
) {
|
||||||
content(accountViewModel)
|
content(accountViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
RelayStatus(
|
|
||||||
{ wantsToEditRelays = true }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -237,7 +221,9 @@ fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewMod
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
RelayIcon { wantsToEditRelays = true }
|
SearchIcon() {
|
||||||
|
nav(Route.Search.route)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Divider(thickness = 0.25.dp)
|
Divider(thickness = 0.25.dp)
|
||||||
@@ -245,80 +231,14 @@ fun GenericTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RelayStatus(
|
private fun SearchIcon(onClick: () -> Unit) {
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
val relayViewModel: RelayPoolViewModel = viewModel { RelayPoolViewModel() }
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.fillMaxHeight(),
|
|
||||||
horizontalAlignment = Alignment.End
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxHeight(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
RelayStatus(relayViewModel, onClick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RelayStatus(
|
|
||||||
relayViewModel: RelayPoolViewModel,
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
val connectedRelaysLiveData = relayViewModel.connectedRelaysLiveData.observeAsState()
|
|
||||||
val availableRelaysLiveData = relayViewModel.availableRelaysLiveData.observeAsState()
|
|
||||||
|
|
||||||
val connectedRelaysText by remember(connectedRelaysLiveData, availableRelaysLiveData) {
|
|
||||||
derivedStateOf {
|
|
||||||
"${connectedRelaysLiveData.value ?: "--"}/${availableRelaysLiveData.value ?: "--"}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isConnected by remember(connectedRelaysLiveData) {
|
|
||||||
derivedStateOf {
|
|
||||||
(connectedRelaysLiveData.value ?: 0) > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderRelayStatus(connectedRelaysText, isConnected, onClick)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RenderRelayStatus(
|
|
||||||
connectedRelaysText: String,
|
|
||||||
isConnected: Boolean,
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = connectedRelaysText,
|
|
||||||
color = if (isConnected) {
|
|
||||||
MaterialTheme.colors.onSurface.copy(
|
|
||||||
alpha = 0.32f
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Color.Red
|
|
||||||
},
|
|
||||||
style = MaterialTheme.typography.subtitle1,
|
|
||||||
modifier = Modifier.clickable(
|
|
||||||
onClick = onClick
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RelayIcon(onClick: () -> Unit) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onClick,
|
onClick = onClick
|
||||||
modifier = Modifier
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.relays),
|
painter = painterResource(R.drawable.ic_search),
|
||||||
null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(24.dp),
|
modifier = Modifier.size(22.dp),
|
||||||
tint = Color.Unspecified
|
tint = Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -534,7 +454,6 @@ fun AmethystIcon() {
|
|||||||
NostrChannelDataSource.printCounter()
|
NostrChannelDataSource.printCounter()
|
||||||
NostrChatroomDataSource.printCounter()
|
NostrChatroomDataSource.printCounter()
|
||||||
NostrChatroomListDataSource.printCounter()
|
NostrChatroomListDataSource.printCounter()
|
||||||
NostrGlobalDataSource.printCounter()
|
|
||||||
NostrHashtagDataSource.printCounter()
|
NostrHashtagDataSource.printCounter()
|
||||||
NostrHomeDataSource.printCounter()
|
NostrHomeDataSource.printCounter()
|
||||||
NostrSearchEventOrUserDataSource.printCounter()
|
NostrSearchEventOrUserDataSource.printCounter()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import androidx.compose.material.TextButton
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -52,6 +53,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
@@ -60,13 +62,17 @@ import com.vitorpamplona.amethyst.ServiceManager
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.HttpClient
|
import com.vitorpamplona.amethyst.service.HttpClient
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
|
||||||
import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists
|
import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists
|
||||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size16dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -301,6 +307,12 @@ fun ListContent(
|
|||||||
var conectOrbotDialogOpen by remember { mutableStateOf(false) }
|
var conectOrbotDialogOpen by remember { mutableStateOf(false) }
|
||||||
var proxyPort = remember { mutableStateOf(account.proxyPort.toString()) }
|
var proxyPort = remember { mutableStateOf(account.proxyPort.toString()) }
|
||||||
|
|
||||||
|
val relayViewModel: RelayPoolViewModel = viewModel { RelayPoolViewModel() }
|
||||||
|
|
||||||
|
var wantsToEditRelays by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
@@ -324,6 +336,16 @@ fun ListContent(
|
|||||||
route = Route.Bookmarks.route
|
route = Route.Bookmarks.route
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IconRowRelays(
|
||||||
|
relayViewModel = relayViewModel,
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
scaffoldState.drawerState.close()
|
||||||
|
}
|
||||||
|
wantsToEditRelays = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
NavigationRow(
|
NavigationRow(
|
||||||
title = stringResource(R.string.security_filters),
|
title = stringResource(R.string.security_filters),
|
||||||
icon = Route.BlockedUsers.icon,
|
icon = Route.BlockedUsers.icon,
|
||||||
@@ -431,6 +453,10 @@ fun ListContent(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wantsToEditRelays) {
|
||||||
|
NewRelayListView({ wantsToEditRelays = false }, accountViewModel, nav = nav)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableTor(
|
private fun enableTor(
|
||||||
@@ -446,6 +472,44 @@ private fun enableTor(
|
|||||||
ServiceManager.start(context)
|
ServiceManager.start(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RelayStatus(
|
||||||
|
relayViewModel: RelayPoolViewModel
|
||||||
|
) {
|
||||||
|
val connectedRelaysLiveData = relayViewModel.connectedRelaysLiveData.observeAsState()
|
||||||
|
val availableRelaysLiveData = relayViewModel.availableRelaysLiveData.observeAsState()
|
||||||
|
|
||||||
|
val connectedRelaysText by remember(connectedRelaysLiveData, availableRelaysLiveData) {
|
||||||
|
derivedStateOf {
|
||||||
|
"${connectedRelaysLiveData.value ?: "--"}/${availableRelaysLiveData.value ?: "--"}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isConnected by remember(connectedRelaysLiveData) {
|
||||||
|
derivedStateOf {
|
||||||
|
(connectedRelaysLiveData.value ?: 0) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderRelayStatus(connectedRelaysText, isConnected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RenderRelayStatus(
|
||||||
|
connectedRelaysText: String,
|
||||||
|
isConnected: Boolean
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = connectedRelaysText,
|
||||||
|
color = if (isConnected) {
|
||||||
|
MaterialTheme.colors.placeholderText
|
||||||
|
} else {
|
||||||
|
Color.Red
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.subtitle1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavigationRow(
|
fun NavigationRow(
|
||||||
title: String,
|
title: String,
|
||||||
@@ -496,6 +560,39 @@ fun IconRow(title: String, icon: Int, tint: Color, onClick: () -> Unit, onLongCl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun IconRowRelays(relayViewModel: RelayPoolViewModel, onClick: () -> Unit) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick() }
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 15.dp, horizontal = 25.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.relays),
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(22.dp),
|
||||||
|
tint = MaterialTheme.colors.onSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 16.dp),
|
||||||
|
text = stringResource(id = R.string.relays),
|
||||||
|
fontSize = 18.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(Size16dp))
|
||||||
|
|
||||||
|
RelayStatus(relayViewModel = relayViewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Unit) {
|
fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Unit) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|||||||
@@ -42,9 +42,14 @@ sealed class Route(
|
|||||||
hasNewItems = { accountViewModel, newNotes -> HomeLatestItem.hasNewItems(accountViewModel, newNotes) }
|
hasNewItems = { accountViewModel, newNotes -> HomeLatestItem.hasNewItems(accountViewModel, newNotes) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object Global : Route(
|
||||||
|
route = "Global",
|
||||||
|
icon = R.drawable.ic_globe
|
||||||
|
)
|
||||||
|
|
||||||
object Search : Route(
|
object Search : Route(
|
||||||
route = "Search",
|
route = "Search",
|
||||||
icon = R.drawable.ic_globe
|
icon = R.drawable.ic_search
|
||||||
)
|
)
|
||||||
|
|
||||||
object Video : Route(
|
object Video : Route(
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.vitorpamplona.amethyst.ui.dal.ChatroomFeedFilter
|
|||||||
import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.ChatroomListNewFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.ChatroomListNewFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.GlobalFeedFilter
|
|
||||||
import com.vitorpamplona.amethyst.ui.dal.HashtagFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.HashtagFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.HomeLiveActivitiesFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.HomeLiveActivitiesFeedFilter
|
||||||
@@ -59,13 +58,6 @@ class NostrChatroomFeedViewModel(val user: User, val account: Account) : FeedVie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NostrGlobalFeedViewModel(val account: Account) : FeedViewModel(GlobalFeedFilter(account)) {
|
|
||||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
|
||||||
override fun <NostrGlobalFeedViewModel : ViewModel> create(modelClass: Class<NostrGlobalFeedViewModel>): NostrGlobalFeedViewModel {
|
|
||||||
return NostrGlobalFeedViewModel(account) as NostrGlobalFeedViewModel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class NostrVideoFeedViewModel(val account: Account) : FeedViewModel(VideoFeedFilter(account)) {
|
class NostrVideoFeedViewModel(val account: Account) : FeedViewModel(VideoFeedFilter(account)) {
|
||||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||||
override fun <NostrVideoFeedViewModel : ViewModel> create(modelClass: Class<NostrVideoFeedViewModel>): NostrVideoFeedViewModel {
|
override fun <NostrVideoFeedViewModel : ViewModel> create(modelClass: Class<NostrVideoFeedViewModel>): NostrVideoFeedViewModel {
|
||||||
@@ -201,8 +193,8 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
|||||||
fun refreshSuspended() {
|
fun refreshSuspended() {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val notes = localFilter.loadTop().toImmutableList()
|
|
||||||
lastFeedKey = localFilter.feedKey()
|
lastFeedKey = localFilter.feedKey()
|
||||||
|
val notes = localFilter.loadTop().toImmutableList()
|
||||||
|
|
||||||
val oldNotesState = _feedContent.value
|
val oldNotesState = _feedContent.value
|
||||||
if (oldNotesState is FeedState.Loaded) {
|
if (oldNotesState is FeedState.Loaded) {
|
||||||
@@ -231,16 +223,14 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
|||||||
|
|
||||||
fun refreshFromOldState(newItems: Set<Note>) {
|
fun refreshFromOldState(newItems: Set<Note>) {
|
||||||
val oldNotesState = _feedContent.value
|
val oldNotesState = _feedContent.value
|
||||||
if (localFilter is AdditiveFeedFilter) {
|
if (localFilter is AdditiveFeedFilter && lastFeedKey != localFilter.feedKey()) {
|
||||||
if (oldNotesState is FeedState.Loaded) {
|
if (oldNotesState is FeedState.Loaded) {
|
||||||
val newList = localFilter.updateListWith(oldNotesState.feed.value, newItems.toSet()).toImmutableList()
|
val newList = localFilter.updateListWith(oldNotesState.feed.value, newItems.toSet()).toImmutableList()
|
||||||
lastFeedKey = localFilter.feedKey()
|
|
||||||
if (!equalImmutableLists(newList, oldNotesState.feed.value)) {
|
if (!equalImmutableLists(newList, oldNotesState.feed.value)) {
|
||||||
updateFeed(newList)
|
updateFeed(newList)
|
||||||
}
|
}
|
||||||
} else if (oldNotesState is FeedState.Empty) {
|
} else if (oldNotesState is FeedState.Empty) {
|
||||||
val newList = localFilter.updateListWith(emptyList(), newItems.toSet()).toImmutableList()
|
val newList = localFilter.updateListWith(emptyList(), newItems.toSet()).toImmutableList()
|
||||||
lastFeedKey = localFilter.feedKey()
|
|
||||||
if (newList.isNotEmpty()) {
|
if (newList.isNotEmpty()) {
|
||||||
updateFeed(newList)
|
updateFeed(newList)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,13 +38,11 @@ import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
|||||||
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
|
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.DrawerContent
|
import com.vitorpamplona.amethyst.ui.navigation.DrawerContent
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.currentRoute
|
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
|
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountState
|
import com.vitorpamplona.amethyst.ui.screen.AccountState
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedLiveActivitiesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedLiveActivitiesViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
|
||||||
@@ -95,11 +93,6 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
factory = NostrHomeFeedLiveActivitiesViewModel.Factory(accountViewModel.account)
|
factory = NostrHomeFeedLiveActivitiesViewModel.Factory(accountViewModel.account)
|
||||||
)
|
)
|
||||||
|
|
||||||
val searchFeedViewModel: NostrGlobalFeedViewModel = viewModel(
|
|
||||||
key = accountViewModel.userProfile().pubkeyHex + "NostrGlobalFeedViewModel",
|
|
||||||
factory = NostrGlobalFeedViewModel.Factory(accountViewModel.account)
|
|
||||||
)
|
|
||||||
|
|
||||||
val videoFeedViewModel: NostrVideoFeedViewModel = viewModel(
|
val videoFeedViewModel: NostrVideoFeedViewModel = viewModel(
|
||||||
key = accountViewModel.userProfile().pubkeyHex + "NostrVideoFeedViewModel",
|
key = accountViewModel.userProfile().pubkeyHex + "NostrVideoFeedViewModel",
|
||||||
factory = NostrVideoFeedViewModel.Factory(accountViewModel.account)
|
factory = NostrVideoFeedViewModel.Factory(accountViewModel.account)
|
||||||
@@ -140,9 +133,6 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
homeFeedViewModel.sendToTop()
|
homeFeedViewModel.sendToTop()
|
||||||
repliesFeedViewModel.sendToTop()
|
repliesFeedViewModel.sendToTop()
|
||||||
}
|
}
|
||||||
Route.Search.base -> {
|
|
||||||
searchFeedViewModel.sendToTop()
|
|
||||||
}
|
|
||||||
Route.Video.base -> {
|
Route.Video.base -> {
|
||||||
videoFeedViewModel.sendToTop()
|
videoFeedViewModel.sendToTop()
|
||||||
}
|
}
|
||||||
@@ -193,7 +183,6 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
liveActivitiesViewModel,
|
liveActivitiesViewModel,
|
||||||
knownFeedViewModel,
|
knownFeedViewModel,
|
||||||
newFeedViewModel,
|
newFeedViewModel,
|
||||||
searchFeedViewModel,
|
|
||||||
videoFeedViewModel,
|
videoFeedViewModel,
|
||||||
notifFeedViewModel,
|
notifFeedViewModel,
|
||||||
userReactionsStatsModel,
|
userReactionsStatsModel,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@@ -56,7 +57,6 @@ import com.vitorpamplona.amethyst.model.Channel
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
||||||
@@ -66,9 +66,6 @@ import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
|||||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
@@ -86,7 +83,6 @@ import kotlinx.coroutines.channels.Channel as CoroutineChannel
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchScreen(
|
fun SearchScreen(
|
||||||
searchFeedViewModel: NostrGlobalFeedViewModel,
|
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -97,33 +93,29 @@ fun SearchScreen(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
SearchScreen(searchFeedViewModel, searchBarViewModel, accountViewModel, nav)
|
SearchScreen(searchBarViewModel, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchScreen(
|
fun SearchScreen(
|
||||||
searchFeedViewModel: NostrGlobalFeedViewModel,
|
|
||||||
searchBarViewModel: SearchBarViewModel,
|
searchBarViewModel: SearchBarViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
WatchAccountForSearchScreen(searchFeedViewModel, accountViewModel)
|
WatchAccountForSearchScreen(accountViewModel)
|
||||||
|
|
||||||
DisposableEffect(accountViewModel) {
|
DisposableEffect(accountViewModel) {
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
if (event == Lifecycle.Event.ON_RESUME) {
|
if (event == Lifecycle.Event.ON_RESUME) {
|
||||||
println("Global Start")
|
println("Search Start")
|
||||||
NostrGlobalDataSource.start()
|
|
||||||
NostrSearchEventOrUserDataSource.start()
|
NostrSearchEventOrUserDataSource.start()
|
||||||
searchFeedViewModel.invalidateData()
|
|
||||||
}
|
}
|
||||||
if (event == Lifecycle.Event.ON_PAUSE) {
|
if (event == Lifecycle.Event.ON_PAUSE) {
|
||||||
println("Global Stop")
|
println("Search Stop")
|
||||||
NostrSearchEventOrUserDataSource.clear()
|
NostrSearchEventOrUserDataSource.clear()
|
||||||
NostrSearchEventOrUserDataSource.stop()
|
NostrSearchEventOrUserDataSource.stop()
|
||||||
NostrGlobalDataSource.stop()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,29 +125,19 @@ fun SearchScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxHeight()) {
|
val listState = rememberLazyListState()
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
Column(Modifier.fillMaxSize()) {
|
||||||
) {
|
SearchBar(searchBarViewModel, listState)
|
||||||
SearchBar(searchBarViewModel, accountViewModel, nav)
|
DisplaySearchResults(searchBarViewModel, listState, nav, accountViewModel)
|
||||||
RefresheableFeedView(
|
|
||||||
searchFeedViewModel,
|
|
||||||
null,
|
|
||||||
scrollStateKey = ScrollStateKeys.GLOBAL_SCREEN,
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
nav = nav
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WatchAccountForSearchScreen(searchFeedViewModel: NostrGlobalFeedViewModel, accountViewModel: AccountViewModel) {
|
fun WatchAccountForSearchScreen(accountViewModel: AccountViewModel) {
|
||||||
LaunchedEffect(accountViewModel) {
|
LaunchedEffect(accountViewModel) {
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
NostrGlobalDataSource.resetFilters()
|
|
||||||
NostrSearchEventOrUserDataSource.start()
|
NostrSearchEventOrUserDataSource.start()
|
||||||
searchFeedViewModel.invalidateData(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,11 +210,9 @@ class SearchBarViewModel(val account: Account) : ViewModel() {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SearchBar(
|
private fun SearchBar(
|
||||||
searchBarViewModel: SearchBarViewModel,
|
searchBarViewModel: SearchBarViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
listState: LazyListState
|
||||||
nav: (String) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val listState = rememberLazyListState()
|
|
||||||
|
|
||||||
// Create a channel for processing search queries.
|
// Create a channel for processing search queries.
|
||||||
val searchTextChanges = remember {
|
val searchTextChanges = remember {
|
||||||
@@ -283,8 +263,6 @@ private fun SearchBar(
|
|||||||
searchTextChanges.trySend(it)
|
searchTextChanges.trySend(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplaySearchResults(listState, searchBarViewModel, nav, accountViewModel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -352,8 +330,8 @@ private fun SearchTextField(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisplaySearchResults(
|
private fun DisplaySearchResults(
|
||||||
listState: LazyListState,
|
|
||||||
searchBarViewModel: SearchBarViewModel,
|
searchBarViewModel: SearchBarViewModel,
|
||||||
|
listState: LazyListState,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -468,4 +468,6 @@
|
|||||||
<string name="live_stream_has_ended">Livestream Ended</string>
|
<string name="live_stream_has_ended">Livestream Ended</string>
|
||||||
<string name="are_you_sure_you_want_to_log_out">Logging out deletes all your local information. Make sure to have your private keys backed up to avoid losing your account. Do you want to continue?</string>
|
<string name="are_you_sure_you_want_to_log_out">Logging out deletes all your local information. Make sure to have your private keys backed up to avoid losing your account. Do you want to continue?</string>
|
||||||
<string name="followed_tags">Followed Tags</string>
|
<string name="followed_tags">Followed Tags</string>
|
||||||
|
|
||||||
|
<string name="relay_setup">Relays</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user