@@ -80,7 +80,7 @@ class ImageUploadTesting {
|
|||||||
.addInterceptor(DefaultContentTypeInterceptor("Amethyst/${BuildConfig.VERSION_NAME}"))
|
.addInterceptor(DefaultContentTypeInterceptor("Amethyst/${BuildConfig.VERSION_NAME}"))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private suspend fun getBitmap(): ByteArray {
|
private fun getBitmap(): ByteArray {
|
||||||
val bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
|
val bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
|
||||||
for (x in 0 until bitmap.width) {
|
for (x in 0 until bitmap.width) {
|
||||||
for (y in 0 until bitmap.height) {
|
for (y in 0 until bitmap.height) {
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ class AppModules(
|
|||||||
val relayStats = RelayStats(client)
|
val relayStats = RelayStats(client)
|
||||||
|
|
||||||
// Logs debug messages when needed
|
// Logs debug messages when needed
|
||||||
val detailedLogger = if (isDebug) RelayLogger(client, false, false) else null
|
val detailedLogger = if (isDebug) RelayLogger(client, debugSending = false, debugReceiving = false) else null
|
||||||
val relayReqStats = if (isDebug) RelayReqStats(client) else null
|
val relayReqStats = if (isDebug) RelayReqStats(client) else null
|
||||||
val logger = if (isDebug) RelaySpeedLogger(client) else null
|
val logger = if (isDebug) RelaySpeedLogger(client) else null
|
||||||
|
|
||||||
|
|||||||
@@ -186,8 +186,8 @@ class AccountSettings(
|
|||||||
var backupEphemeralChatList: EphemeralChatListEvent? = null,
|
var backupEphemeralChatList: EphemeralChatListEvent? = null,
|
||||||
var backupTrustProviderList: TrustProviderListEvent? = null,
|
var backupTrustProviderList: TrustProviderListEvent? = null,
|
||||||
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
||||||
val hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf<String>()),
|
val hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf()),
|
||||||
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow<Map<HexKey, String>>(mapOf()),
|
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow(mapOf()),
|
||||||
var backupNipA3PaymentTargets: PaymentTargetsEvent? = null,
|
var backupNipA3PaymentTargets: PaymentTargetsEvent? = null,
|
||||||
) : EphemeralChatRepository,
|
) : EphemeralChatRepository,
|
||||||
PublicChatListRepository {
|
PublicChatListRepository {
|
||||||
@@ -612,7 +612,7 @@ class AccountSettings(
|
|||||||
route: String,
|
route: String,
|
||||||
timestampInSecs: Long,
|
timestampInSecs: Long,
|
||||||
): MutableStateFlow<Long> =
|
): MutableStateFlow<Long> =
|
||||||
MutableStateFlow<Long>(timestampInSecs).also { newFlow ->
|
MutableStateFlow(timestampInSecs).also { newFlow ->
|
||||||
lastReadPerRoute.update { it + Pair(route, newFlow) }
|
lastReadPerRoute.update { it + Pair(route, newFlow) }
|
||||||
saveAccountSettings()
|
saveAccountSettings()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class AntiSpamFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flowSpam = MutableStateFlow<AntiSpamState>(AntiSpamState(this))
|
val flowSpam = MutableStateFlow(AntiSpamState(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
class AntiSpamState(
|
class AntiSpamState(
|
||||||
|
|||||||
@@ -25,14 +25,13 @@ import java.lang.ref.WeakReference
|
|||||||
import java.util.concurrent.ConcurrentSkipListMap
|
import java.util.concurrent.ConcurrentSkipListMap
|
||||||
import java.util.function.BiConsumer
|
import java.util.function.BiConsumer
|
||||||
|
|
||||||
class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
class LargeSoftCache<K : Any, V : Any> : CacheOperations<K, V> {
|
||||||
protected val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
|
private val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
|
||||||
|
|
||||||
fun keys() = cache.keys
|
fun keys() = cache.keys
|
||||||
|
|
||||||
fun get(key: K): V? {
|
fun get(key: K): V? {
|
||||||
val softRef = cache.get(key)
|
val softRef = cache.get(key) ?: return null
|
||||||
if (softRef == null) return null
|
|
||||||
val value = softRef.get()
|
val value = softRef.get()
|
||||||
|
|
||||||
return if (value != null) {
|
return if (value != null) {
|
||||||
@@ -99,7 +98,7 @@ class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
|||||||
// another thread may put in between
|
// another thread may put in between
|
||||||
cache.remove(key, softRef)
|
cache.remove(key, softRef)
|
||||||
val newObject = builder(key)
|
val newObject = builder(key)
|
||||||
return cache.putIfAbsent(key, WeakReference(newObject))?.get() ?: newObject
|
cache.putIfAbsent(key, WeakReference(newObject))?.get() ?: newObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +134,7 @@ class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
|||||||
.forEach(BiConsumerWrapper(this, consumer))
|
.forEach(BiConsumerWrapper(this, consumer))
|
||||||
}
|
}
|
||||||
|
|
||||||
class BiConsumerWrapper<K, V>(
|
class BiConsumerWrapper<K : Any, V : Any>(
|
||||||
val cache: LargeSoftCache<K, V>,
|
val cache: LargeSoftCache<K, V>,
|
||||||
val inner: BiConsumer<K, V>,
|
val inner: BiConsumer<K, V>,
|
||||||
) : BiConsumer<K, WeakReference<V>> {
|
) : BiConsumer<K, WeakReference<V>> {
|
||||||
|
|||||||
+4
-4
@@ -34,9 +34,9 @@ class TorAwareOkHttpOtsResolverBuilder(
|
|||||||
) : OtsResolverBuilder {
|
) : OtsResolverBuilder {
|
||||||
fun getAPI(usingTor: Boolean) =
|
fun getAPI(usingTor: Boolean) =
|
||||||
if (usingTor) {
|
if (usingTor) {
|
||||||
OkHttpBitcoinExplorer.Companion.MEMPOOL_API_URL
|
OkHttpBitcoinExplorer.MEMPOOL_API_URL
|
||||||
} else {
|
} else {
|
||||||
OkHttpBitcoinExplorer.Companion.BLOCKSTREAM_API_URL
|
OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun build(): OtsResolver =
|
override fun build(): OtsResolver =
|
||||||
@@ -44,9 +44,9 @@ class TorAwareOkHttpOtsResolverBuilder(
|
|||||||
explorer =
|
explorer =
|
||||||
OkHttpBitcoinExplorer(
|
OkHttpBitcoinExplorer(
|
||||||
baseUrl = {
|
baseUrl = {
|
||||||
getAPI(usingTor = isTorActive(OkHttpBitcoinExplorer.Companion.MEMPOOL_API_URL))
|
getAPI(usingTor = isTorActive(OkHttpBitcoinExplorer.MEMPOOL_API_URL))
|
||||||
},
|
},
|
||||||
client = okHttpClient(OkHttpBitcoinExplorer.Companion.MEMPOOL_API_URL),
|
client = okHttpClient(OkHttpBitcoinExplorer.MEMPOOL_API_URL),
|
||||||
cache = cache,
|
cache = cache,
|
||||||
),
|
),
|
||||||
calendar = OkHttpCalendar(okHttpClient),
|
calendar = OkHttpCalendar(okHttpClient),
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ class BookmarkListState(
|
|||||||
|
|
||||||
fun getBookmarkList(): BookmarkListEvent? = bookmarkList.event as? BookmarkListEvent
|
fun getBookmarkList(): BookmarkListEvent? = bookmarkList.event as? BookmarkListEvent
|
||||||
|
|
||||||
suspend fun publicBookmarks(note: Note): List<BookmarkIdTag> {
|
fun publicBookmarks(note: Note): List<BookmarkIdTag> {
|
||||||
val noteEvent = note.event as? BookmarkListEvent
|
val noteEvent = note.event as? BookmarkListEvent
|
||||||
return noteEvent?.publicBookmarks() ?: emptyList()
|
return noteEvent?.publicBookmarks() ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ class HiddenUsersState(
|
|||||||
) {
|
) {
|
||||||
var transientHiddenUsers: MutableStateFlow<Set<String>> = MutableStateFlow(setOf())
|
var transientHiddenUsers: MutableStateFlow<Set<String>> = MutableStateFlow(setOf())
|
||||||
|
|
||||||
suspend fun assembleLiveHiddenUsers(
|
fun assembleLiveHiddenUsers(
|
||||||
blockList: List<MuteTag>,
|
blockList: List<MuteTag>,
|
||||||
muteList: List<MuteTag>,
|
muteList: List<MuteTag>,
|
||||||
transientHiddenUsers: Set<String>,
|
transientHiddenUsers: Set<String>,
|
||||||
|
|||||||
+2
-2
@@ -51,7 +51,7 @@ class SearchRelayListState(
|
|||||||
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
|
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
|
||||||
val searchListNote = cache.getOrCreateAddressableNote(getSearchRelayListAddress())
|
val searchListNote = cache.getOrCreateAddressableNote(getSearchRelayListAddress())
|
||||||
|
|
||||||
fun getSearchRelayListAddress() = SearchRelayListEvent.Companion.createAddress(signer.pubKey)
|
fun getSearchRelayListAddress() = SearchRelayListEvent.createAddress(signer.pubKey)
|
||||||
|
|
||||||
fun getSearchRelayListFlow(): StateFlow<NoteState> = searchListNote.flow().metadata.stateFlow
|
fun getSearchRelayListFlow(): StateFlow<NoteState> = searchListNote.flow().metadata.stateFlow
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ class SearchRelayListState(
|
|||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope,
|
scope,
|
||||||
SharingStarted.Companion.Eagerly,
|
SharingStarted.Eagerly,
|
||||||
emptySet(),
|
emptySet(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -68,7 +68,7 @@ class TrustedRelayListState(
|
|||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope,
|
scope,
|
||||||
SharingStarted.Companion.Eagerly,
|
SharingStarted.Eagerly,
|
||||||
emptySet(),
|
emptySet(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,13 +76,13 @@ class TrustedRelayListState(
|
|||||||
val relayListForTrusted = getTrustedRelayList()
|
val relayListForTrusted = getTrustedRelayList()
|
||||||
|
|
||||||
return if (relayListForTrusted != null && relayListForTrusted.tags.isNotEmpty()) {
|
return if (relayListForTrusted != null && relayListForTrusted.tags.isNotEmpty()) {
|
||||||
TrustedRelayListEvent.Companion.updateRelayList(
|
TrustedRelayListEvent.updateRelayList(
|
||||||
earlierVersion = relayListForTrusted,
|
earlierVersion = relayListForTrusted,
|
||||||
relays = trustedRelays,
|
relays = trustedRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
TrustedRelayListEvent.Companion.create(
|
TrustedRelayListEvent.create(
|
||||||
relays = trustedRelays,
|
relays = trustedRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -120,5 +120,5 @@ class BlossomServerListState(
|
|||||||
suspend fun createBlossomDeleteAuth(
|
suspend fun createBlossomDeleteAuth(
|
||||||
hash: HexKey,
|
hash: HexKey,
|
||||||
alt: String,
|
alt: String,
|
||||||
): BlossomAuthorizationEvent? = BlossomAuthorizationEvent.createDeleteAuth(hash, alt, signer)
|
): BlossomAuthorizationEvent = BlossomAuthorizationEvent.createDeleteAuth(hash, alt, signer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ import kotlinx.coroutines.flow.catch
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
suspend fun <T> DataStore<Preferences>.getProperty(
|
fun <T> DataStore<Preferences>.getProperty(
|
||||||
key: Preferences.Key<String>,
|
key: Preferences.Key<String>,
|
||||||
parser: (String) -> T,
|
parser: (String) -> T,
|
||||||
serializer: (T) -> String,
|
serializer: (T) -> String,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
): UpdatablePropertyFlow<T> =
|
): UpdatablePropertyFlow<T> =
|
||||||
UpdatablePropertyFlow<T>(
|
UpdatablePropertyFlow(
|
||||||
flow =
|
flow =
|
||||||
data
|
data
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
|
|||||||
+2
-2
@@ -67,12 +67,12 @@ class EncryptedDataStore(
|
|||||||
?.get(key)
|
?.get(key)
|
||||||
?.let { decrypt(it) }
|
?.let { decrypt(it) }
|
||||||
|
|
||||||
suspend fun <T> getProperty(
|
fun <T> getProperty(
|
||||||
key: Preferences.Key<String>,
|
key: Preferences.Key<String>,
|
||||||
parser: (String) -> T,
|
parser: (String) -> T,
|
||||||
serializer: (T) -> String,
|
serializer: (T) -> String,
|
||||||
): UpdatablePropertyFlow<T> =
|
): UpdatablePropertyFlow<T> =
|
||||||
UpdatablePropertyFlow<T>(
|
UpdatablePropertyFlow(
|
||||||
flow =
|
flow =
|
||||||
store.data
|
store.data
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
|
|||||||
+2
-2
@@ -60,8 +60,8 @@ class MergedFollowListsState(
|
|||||||
val geotags: Set<String> = emptySet(),
|
val geotags: Set<String> = emptySet(),
|
||||||
val communities: Set<String> = emptySet(),
|
val communities: Set<String> = emptySet(),
|
||||||
) {
|
) {
|
||||||
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
|
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mergeLists(
|
fun mergeLists(
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class OutboxLoaderState(
|
|||||||
}.flowOn(Dispatchers.IO)
|
}.flowOn(Dispatchers.IO)
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope,
|
scope,
|
||||||
SharingStarted.Companion.Eagerly,
|
SharingStarted.Eagerly,
|
||||||
UnknownTopNavPerRelayFilterSet,
|
UnknownTopNavPerRelayFilterSet,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -52,8 +52,8 @@ class AllFollowsByOutboxTopNavFilter(
|
|||||||
val defaultRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
val defaultRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||||
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||||
) : IFeedTopNavFilter {
|
) : IFeedTopNavFilter {
|
||||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.Companion.toScope(it) }
|
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.Companion.toScope(it) }
|
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||||
|
|
||||||
override fun matchAuthor(pubkey: HexKey): Boolean = authors == null || pubkey in authors
|
override fun matchAuthor(pubkey: HexKey): Boolean = authors == null || pubkey in authors
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -47,8 +47,8 @@ class AllFollowsByProxyTopNavFilter(
|
|||||||
val communities: Set<String>? = null,
|
val communities: Set<String>? = null,
|
||||||
val proxyRelays: Set<NormalizedRelayUrl>,
|
val proxyRelays: Set<NormalizedRelayUrl>,
|
||||||
) : IFeedTopNavFilter {
|
) : IFeedTopNavFilter {
|
||||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.Companion.toScope(it) }
|
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.Companion.toScope(it) }
|
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||||
|
|
||||||
override fun matchAuthor(pubkey: HexKey): Boolean = authors == null || pubkey in authors
|
override fun matchAuthor(pubkey: HexKey): Boolean = authors == null || pubkey in authors
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -35,6 +35,6 @@ class AllFollowsTopNavPerRelayFilter(
|
|||||||
val geotags: Set<String>? = null,
|
val geotags: Set<String>? = null,
|
||||||
val communities: Set<String>? = null,
|
val communities: Set<String>? = null,
|
||||||
) : IFeedTopNavPerRelayFilter {
|
) : IFeedTopNavPerRelayFilter {
|
||||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
|
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ class LocationTopNavFilter(
|
|||||||
val geotags: Set<String>,
|
val geotags: Set<String>,
|
||||||
val relayList: Set<NormalizedRelayUrl>,
|
val relayList: Set<NormalizedRelayUrl>,
|
||||||
) : IFeedTopNavFilter {
|
) : IFeedTopNavFilter {
|
||||||
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
|
|
||||||
override fun matchAuthor(pubkey: HexKey): Boolean = true
|
override fun matchAuthor(pubkey: HexKey): Boolean = true
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -28,5 +28,5 @@ import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
|||||||
class LocationTopNavPerRelayFilter(
|
class LocationTopNavPerRelayFilter(
|
||||||
val geotags: Set<String>,
|
val geotags: Set<String>,
|
||||||
) : IFeedTopNavPerRelayFilter {
|
) : IFeedTopNavPerRelayFilter {
|
||||||
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -62,7 +62,7 @@ class NoteFeedFlow(
|
|||||||
): IFeedTopNavFilter =
|
): IFeedTopNavFilter =
|
||||||
when (noteEvent) {
|
when (noteEvent) {
|
||||||
is PeopleListEvent -> {
|
is PeopleListEvent -> {
|
||||||
if (noteEvent.dTag() == PeopleListEvent.Companion.BLOCK_LIST_D_TAG) {
|
if (noteEvent.dTag() == PeopleListEvent.BLOCK_LIST_D_TAG) {
|
||||||
MutedAuthorsByOutboxTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), blockedRelays)
|
MutedAuthorsByOutboxTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), blockedRelays)
|
||||||
} else {
|
} else {
|
||||||
AuthorsByOutboxTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), blockedRelays)
|
AuthorsByOutboxTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), blockedRelays)
|
||||||
@@ -109,7 +109,7 @@ class NoteFeedFlow(
|
|||||||
) {
|
) {
|
||||||
when (noteEvent) {
|
when (noteEvent) {
|
||||||
is PeopleListEvent -> {
|
is PeopleListEvent -> {
|
||||||
if (noteEvent.dTag() == PeopleListEvent.Companion.BLOCK_LIST_D_TAG) {
|
if (noteEvent.dTag() == PeopleListEvent.BLOCK_LIST_D_TAG) {
|
||||||
emit(MutedAuthorsByOutboxTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), blockedRelays))
|
emit(MutedAuthorsByOutboxTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), blockedRelays))
|
||||||
} else {
|
} else {
|
||||||
emit(AuthorsByOutboxTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), blockedRelays))
|
emit(AuthorsByOutboxTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), blockedRelays))
|
||||||
@@ -161,7 +161,7 @@ class NoteFeedFlow(
|
|||||||
): IFeedTopNavFilter =
|
): IFeedTopNavFilter =
|
||||||
when (noteEvent) {
|
when (noteEvent) {
|
||||||
is PeopleListEvent -> {
|
is PeopleListEvent -> {
|
||||||
if (noteEvent.dTag() == PeopleListEvent.Companion.BLOCK_LIST_D_TAG) {
|
if (noteEvent.dTag() == PeopleListEvent.BLOCK_LIST_D_TAG) {
|
||||||
MutedAuthorsByProxyTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), proxyRelays)
|
MutedAuthorsByProxyTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), proxyRelays)
|
||||||
} else {
|
} else {
|
||||||
AuthorsByProxyTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), proxyRelays)
|
AuthorsByProxyTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), proxyRelays)
|
||||||
@@ -208,7 +208,7 @@ class NoteFeedFlow(
|
|||||||
) {
|
) {
|
||||||
when (noteEvent) {
|
when (noteEvent) {
|
||||||
is PeopleListEvent -> {
|
is PeopleListEvent -> {
|
||||||
if (noteEvent.dTag() == PeopleListEvent.Companion.BLOCK_LIST_D_TAG) {
|
if (noteEvent.dTag() == PeopleListEvent.BLOCK_LIST_D_TAG) {
|
||||||
emit(MutedAuthorsByProxyTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), proxyRelays))
|
emit(MutedAuthorsByProxyTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), proxyRelays))
|
||||||
} else {
|
} else {
|
||||||
emit(AuthorsByProxyTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), proxyRelays))
|
emit(AuthorsByProxyTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), proxyRelays))
|
||||||
@@ -272,7 +272,7 @@ class NoteFeedFlow(
|
|||||||
override fun startValue(): IFeedTopNavFilter {
|
override fun startValue(): IFeedTopNavFilter {
|
||||||
val noteEvent = metadataFlow.value?.note?.event
|
val noteEvent = metadataFlow.value?.note?.event
|
||||||
return if (noteEvent == null) {
|
return if (noteEvent == null) {
|
||||||
return AuthorsByOutboxTopNavFilter(emptySet(), blockedRelays)
|
AuthorsByOutboxTopNavFilter(emptySet(), blockedRelays)
|
||||||
} else {
|
} else {
|
||||||
if (proxyRelays.value.isEmpty()) {
|
if (proxyRelays.value.isEmpty()) {
|
||||||
processByOutbox(noteEvent, outboxRelays.value)
|
processByOutbox(noteEvent, outboxRelays.value)
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ class MeltProcessor {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun melt(
|
fun melt(
|
||||||
token: CashuToken,
|
token: CashuToken,
|
||||||
lud16: String,
|
lud16: String,
|
||||||
okHttpClient: (String) -> OkHttpClient,
|
okHttpClient: (String) -> OkHttpClient,
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class MemoryTrimmingService(
|
|||||||
) {
|
) {
|
||||||
var isTrimmingMemoryMutex = AtomicBoolean(false)
|
var isTrimmingMemoryMutex = AtomicBoolean(false)
|
||||||
|
|
||||||
private suspend fun doTrim(
|
private fun doTrim(
|
||||||
account: Collection<Account>,
|
account: Collection<Account>,
|
||||||
otherAccounts: List<AccountInfo>,
|
otherAccounts: List<AccountInfo>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
+2
@@ -126,12 +126,14 @@ class LightningAddressResolver {
|
|||||||
okHttpClient: (String) -> OkHttpClient,
|
okHttpClient: (String) -> OkHttpClient,
|
||||||
context: Context,
|
context: Context,
|
||||||
): String {
|
): String {
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext") // URLEncoder.encode is CPU-only, not I/O blocking
|
||||||
val encodedMessage = URLEncoder.encode(message, "utf-8")
|
val encodedMessage = URLEncoder.encode(message, "utf-8")
|
||||||
|
|
||||||
val urlBinder = if (lnCallback.contains("?")) "&" else "?"
|
val urlBinder = if (lnCallback.contains("?")) "&" else "?"
|
||||||
var url = "$lnCallback${urlBinder}amount=$milliSats&comment=$encodedMessage"
|
var url = "$lnCallback${urlBinder}amount=$milliSats&comment=$encodedMessage"
|
||||||
|
|
||||||
if (nostrRequest != null) {
|
if (nostrRequest != null) {
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext") // URLEncoder.encode is CPU-only, not I/O blocking
|
||||||
val encodedNostrRequest = URLEncoder.encode(nostrRequest.toJson(), "utf-8")
|
val encodedNostrRequest = URLEncoder.encode(nostrRequest.toJson(), "utf-8")
|
||||||
url += "&nostr=$encodedNostrRequest"
|
url += "&nostr=$encodedNostrRequest"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class LocationState(
|
|||||||
object Loading : LocationResult()
|
object Loading : LocationResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var hasLocationPermission = MutableStateFlow<Boolean>(false)
|
private var hasLocationPermission = MutableStateFlow(false)
|
||||||
private var latestLocation: LocationResult = LocationResult.Loading
|
private var latestLocation: LocationResult = LocationResult.Loading
|
||||||
|
|
||||||
fun setLocationPermission(newValue: Boolean) {
|
fun setLocationPermission(newValue: Boolean) {
|
||||||
|
|||||||
+2
-1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.service.okhttp
|
|||||||
import okhttp3.internal.concurrent.TaskRunner
|
import okhttp3.internal.concurrent.TaskRunner
|
||||||
import okhttp3.internal.http2.Http2
|
import okhttp3.internal.http2.Http2
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.CopyOnWriteArraySet
|
import java.util.concurrent.CopyOnWriteArraySet
|
||||||
import java.util.logging.ConsoleHandler
|
import java.util.logging.ConsoleHandler
|
||||||
import java.util.logging.Handler
|
import java.util.logging.Handler
|
||||||
@@ -45,7 +46,7 @@ object OkHttpDebugLogging {
|
|||||||
level = Level.FINE
|
level = Level.FINE
|
||||||
formatter =
|
formatter =
|
||||||
object : SimpleFormatter() {
|
object : SimpleFormatter() {
|
||||||
override fun format(record: LogRecord) = String.format("[%1\$tF %1\$tT] %2\$s %n", record.millis, record.message)
|
override fun format(record: LogRecord) = String.format(Locale.ROOT, $$"[%1$tF %1$tT] %2$s %n", record.millis, record.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -102,7 +102,7 @@ fun VideoView(
|
|||||||
) {
|
) {
|
||||||
val automaticallyStartPlayback =
|
val automaticallyStartPlayback =
|
||||||
remember {
|
remember {
|
||||||
mutableStateOf<Boolean>(
|
mutableStateOf(
|
||||||
if (alwaysShowVideo) true else accountViewModel.settings.startVideoPlayback(),
|
if (alwaysShowVideo) true else accountViewModel.settings.startVideoPlayback(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ import com.vitorpamplona.amethyst.service.playback.composable.mainVideo.VideoPla
|
|||||||
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.GetMediaItem
|
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.GetMediaItem
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|
||||||
public val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VideoViewInner(
|
fun VideoViewInner(
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ fun MuteButton(
|
|||||||
) {
|
) {
|
||||||
val holdOn =
|
val holdOn =
|
||||||
remember {
|
remember {
|
||||||
mutableStateOf<Boolean>(
|
mutableStateOf(
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ class IntentExtras {
|
|||||||
data.mimeType?.let { putString("mimeType", it) }
|
data.mimeType?.let { putString("mimeType", it) }
|
||||||
data.aspectRatio?.let { putFloat("aspectRatio", it) }
|
data.aspectRatio?.let { putFloat("aspectRatio", it) }
|
||||||
data.proxyPort?.let { putInt("proxyPort", it) }
|
data.proxyPort?.let { putInt("proxyPort", it) }
|
||||||
data.keepPlaying.let { putBoolean("keepPlaying", it) }
|
putBoolean("keepPlaying", data.keepPlaying)
|
||||||
data.waveformData?.let { putFloatArray("wavefrontData", it.wave.toFloatArray()) }
|
data.waveformData?.let { putFloatArray("wavefrontData", it.wave.toFloatArray()) }
|
||||||
|
|
||||||
bounds?.let { putInt("boundLeft", it.left) }
|
bounds?.let { putInt("boundLeft", it.left) }
|
||||||
|
|||||||
+3
-9
@@ -20,8 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.playback.pip
|
package com.vitorpamplona.amethyst.service.playback.pip
|
||||||
|
|
||||||
import android.content.Context.RECEIVER_EXPORTED
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -34,6 +32,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.ui.compose.ContentFrame
|
import androidx.media3.ui.compose.ContentFrame
|
||||||
import androidx.media3.ui.compose.state.rememberMuteButtonState
|
import androidx.media3.ui.compose.state.rememberMuteButtonState
|
||||||
@@ -91,13 +90,8 @@ fun RegisterControllerReceiver(controllerState: MediaControllerState) {
|
|||||||
onPlayPause = controllerState::togglePlayPause,
|
onPlayPause = controllerState::togglePlayPause,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
ContextCompat.registerReceiver(context, receiver, receiver.filterMute, ContextCompat.RECEIVER_EXPORTED)
|
||||||
context.registerReceiver(receiver, receiver.filterMute, RECEIVER_EXPORTED)
|
ContextCompat.registerReceiver(context, receiver, receiver.filterPlayPause, ContextCompat.RECEIVER_EXPORTED)
|
||||||
context.registerReceiver(receiver, receiver.filterPlayPause, RECEIVER_EXPORTED)
|
|
||||||
} else {
|
|
||||||
context.registerReceiver(receiver, receiver.filterMute)
|
|
||||||
context.registerReceiver(receiver, receiver.filterPlayPause)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDispose {
|
onDispose {
|
||||||
context.unregisterReceiver(receiver)
|
context.unregisterReceiver(receiver)
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ import okhttp3.OkHttpClient
|
|||||||
class ExoPlayerBuilder(
|
class ExoPlayerBuilder(
|
||||||
val okHttp: OkHttpClient,
|
val okHttp: OkHttpClient,
|
||||||
) {
|
) {
|
||||||
fun build(context: Context) =
|
fun build(context: Context): ExoPlayer =
|
||||||
ExoPlayer
|
ExoPlayer
|
||||||
.Builder(context)
|
.Builder(context)
|
||||||
.apply {
|
.apply {
|
||||||
|
|||||||
+2
-2
@@ -26,7 +26,7 @@ class ListWithUniqueSetCache<T, U>(
|
|||||||
val key: (T) -> U,
|
val key: (T) -> U,
|
||||||
) {
|
) {
|
||||||
private val list = AtomicReference(listOf<T>())
|
private val list = AtomicReference(listOf<T>())
|
||||||
private val cacheSet = AtomicReference<Set<U>?>(setOf<U>())
|
private val cacheSet = AtomicReference<Set<U>?>(setOf())
|
||||||
|
|
||||||
fun isEmpty() = list.get().isEmpty()
|
fun isEmpty() = list.get().isEmpty()
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class ListWithUniqueSetCache<T, U>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute and attempt to atomically update the cache
|
// Compute and attempt to atomically update the cache
|
||||||
val newSet = list.get().mapTo(mutableSetOf<U>(), key)
|
val newSet = list.get().mapTo(mutableSetOf(), key)
|
||||||
cacheSet.compareAndSet(currentSet, newSet)
|
cacheSet.compareAndSet(currentSet, newSet)
|
||||||
return newSet
|
return newSet
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand
|
package com.vitorpamplona.amethyst.service.relayClient.reqCommand
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManagerControls
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssembler
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssembler
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.ChannelFinderFilterAssemblyGroup
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.ChannelFinderFilterAssemblyGroup
|
||||||
@@ -88,7 +87,7 @@ class RelaySubscriptionsCoordinator(
|
|||||||
val nwc = NWCPaymentFilterAssembler(client)
|
val nwc = NWCPaymentFilterAssembler(client)
|
||||||
|
|
||||||
val all =
|
val all =
|
||||||
listOf<ComposeSubscriptionManagerControls>(
|
listOf(
|
||||||
account,
|
account,
|
||||||
home,
|
home,
|
||||||
chatroomList,
|
chatroomList,
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ class AccountFollowsLoaderSubAssembler(
|
|||||||
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
|
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
|
||||||
* and reports, but only from trusted accounts (follows of all logged in users).
|
* and reports, but only from trusted accounts (follows of all logged in users).
|
||||||
*/
|
*/
|
||||||
val hasTried: EOSEAccountFast<User> = EOSEAccountFast<User>(2000)
|
val hasTried: EOSEAccountFast<User> = EOSEAccountFast(2000)
|
||||||
|
|
||||||
// updates all filters
|
// updates all filters
|
||||||
override fun invalidateFilters(ignoreIfDoing: Boolean) {
|
override fun invalidateFilters(ignoreIfDoing: Boolean) {
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ class AccountNotificationsEoseFromInboxRelaysManager(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: AccountQueryState,
|
key: AccountQueryState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? =
|
): List<RelayBasedFilter> =
|
||||||
key.account.notificationRelays.flow.value.flatMap {
|
key.account.notificationRelays.flow.value.flatMap {
|
||||||
filterSummaryNotificationsToPubkey(
|
filterSummaryNotificationsToPubkey(
|
||||||
relay = it,
|
relay = it,
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@ class AccountNotificationsEoseFromRandomRelaysManager(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: AccountQueryState,
|
key: AccountQueryState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? {
|
): List<RelayBasedFilter> {
|
||||||
// only loads this after the feed is built
|
// only loads this after the feed is built
|
||||||
val defaultSince = key.feedContentStates.notifications.lastNoteCreatedAtIfFilled() ?: TimeUtils.oneWeekAgo()
|
val defaultSince = key.feedContentStates.notifications.lastNoteCreatedAtIfFilled() ?: TimeUtils.oneWeekAgo()
|
||||||
return (key.account.followsPerRelay.value.keys - key.account.notificationRelays.flow.value).flatMap {
|
return (key.account.followsPerRelay.value.keys - key.account.notificationRelays.flow.value).flatMap {
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ class ChannelLoaderSubAssembler(
|
|||||||
client: INostrClient,
|
client: INostrClient,
|
||||||
allKeys: () -> Set<ChannelFinderQueryState>,
|
allKeys: () -> Set<ChannelFinderQueryState>,
|
||||||
) : SingleSubNoEoseCacheEoseManager<ChannelFinderQueryState>(client, allKeys, invalidateAfterEose = true) {
|
) : SingleSubNoEoseCacheEoseManager<ChannelFinderQueryState>(client, allKeys, invalidateAfterEose = true) {
|
||||||
override fun updateFilter(keys: List<ChannelFinderQueryState>): List<RelayBasedFilter>? = filterMissingChannelsById(keys)
|
override fun updateFilter(keys: List<ChannelFinderQueryState>): List<RelayBasedFilter> = filterMissingChannelsById(keys)
|
||||||
|
|
||||||
override fun distinct(key: ChannelFinderQueryState) = key.channel
|
override fun distinct(key: ChannelFinderQueryState) = key.channel
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl>
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterMissingAddressables(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
fun filterMissingAddressables(keys: List<EventFinderQueryState>): List<RelayBasedFilter> {
|
||||||
val addressesPerRelay =
|
val addressesPerRelay =
|
||||||
mapOfSet {
|
mapOfSet {
|
||||||
keys.forEach { key ->
|
keys.forEach { key ->
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@ fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter> {
|
||||||
val eventsPerRelay =
|
val eventsPerRelay =
|
||||||
mapOfSet {
|
mapOfSet {
|
||||||
keys.forEach { key ->
|
keys.forEach { key ->
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ class EventWatcherSubAssembler(
|
|||||||
allKeys: () -> Set<EventFinderQueryState>,
|
allKeys: () -> Set<EventFinderQueryState>,
|
||||||
) : SingleSubEoseManager<EventFinderQueryState>(client, allKeys) {
|
) : SingleSubEoseManager<EventFinderQueryState>(client, allKeys) {
|
||||||
var lastNotesOnFilter = emptyList<Note>()
|
var lastNotesOnFilter = emptyList<Note>()
|
||||||
var latestEOSEs: EOSEAccountFast<Note> = EOSEAccountFast<Note>(1000)
|
var latestEOSEs: EOSEAccountFast<Note> = EOSEAccountFast(1000)
|
||||||
|
|
||||||
override fun newEose(
|
override fun newEose(
|
||||||
relay: NormalizedRelayUrl,
|
relay: NormalizedRelayUrl,
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ class UserOutboxFinderSubAssembler(
|
|||||||
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
|
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
|
||||||
* and reports, but only from trusted accounts (follows of all logged in users).
|
* and reports, but only from trusted accounts (follows of all logged in users).
|
||||||
*/
|
*/
|
||||||
var hasTried: EOSEAccountFast<User> = EOSEAccountFast<User>(200)
|
var hasTried: EOSEAccountFast<User> = EOSEAccountFast(200)
|
||||||
|
|
||||||
fun newEose(
|
fun newEose(
|
||||||
relay: NormalizedRelayUrl,
|
relay: NormalizedRelayUrl,
|
||||||
|
|||||||
+2
-2
@@ -30,8 +30,8 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
|||||||
class KindGroup(
|
class KindGroup(
|
||||||
var count: AtomicInteger = AtomicInteger(0),
|
var count: AtomicInteger = AtomicInteger(0),
|
||||||
var memory: AtomicInteger = AtomicInteger(0),
|
var memory: AtomicInteger = AtomicInteger(0),
|
||||||
val subs: LargeCache<String, AtomicInteger> = LargeCache<String, AtomicInteger>(),
|
val subs: LargeCache<String, AtomicInteger> = LargeCache(),
|
||||||
val relays: LargeCache<NormalizedRelayUrl, AtomicInteger> = LargeCache<NormalizedRelayUrl, AtomicInteger>(),
|
val relays: LargeCache<NormalizedRelayUrl, AtomicInteger> = LargeCache(),
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val MB: Int = 1024
|
const val MB: Int = 1024
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ typealias MutableTime = com.vitorpamplona.amethyst.commons.relays.MutableTime
|
|||||||
open class EOSEByKey<U : Any>(
|
open class EOSEByKey<U : Any>(
|
||||||
cacheSize: Int = 200,
|
cacheSize: Int = 200,
|
||||||
) {
|
) {
|
||||||
var followList: LruCache<U, EOSERelayList> = LruCache<U, EOSERelayList>(cacheSize)
|
var followList: LruCache<U, EOSERelayList> = LruCache(cacheSize)
|
||||||
|
|
||||||
fun addOrUpdate(
|
fun addOrUpdate(
|
||||||
listCode: U,
|
listCode: U,
|
||||||
@@ -99,7 +99,7 @@ open class EOSEAccountKey<U : Any>(
|
|||||||
class EOSEAccountFast<T : Any>(
|
class EOSEAccountFast<T : Any>(
|
||||||
cacheSize: Int = 20,
|
cacheSize: Int = 20,
|
||||||
) {
|
) {
|
||||||
private val users: LruCache<T, EOSERelayList> = LruCache<T, EOSERelayList>(cacheSize)
|
private val users: LruCache<T, EOSERelayList> = LruCache(cacheSize)
|
||||||
private val lock = Any()
|
private val lock = Any()
|
||||||
|
|
||||||
fun addOrUpdate(
|
fun addOrUpdate(
|
||||||
|
|||||||
+7
-10
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.service.tts
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
import android.os.Build
|
|
||||||
import android.speech.tts.TextToSpeech
|
import android.speech.tts.TextToSpeech
|
||||||
import android.speech.tts.UtteranceProgressListener
|
import android.speech.tts.UtteranceProgressListener
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -79,15 +78,13 @@ class TextToSpeechEngine private constructor() {
|
|||||||
engine.setPitch(defaultPitch)
|
engine.setPitch(defaultPitch)
|
||||||
engine.setSpeechRate(defaultSpeed)
|
engine.setSpeechRate(defaultSpeed)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
val audioAttributes =
|
||||||
val audioAttributes =
|
AudioAttributes
|
||||||
AudioAttributes
|
.Builder()
|
||||||
.Builder()
|
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
.build()
|
||||||
.build()
|
engine.setAudioAttributes(audioAttributes)
|
||||||
engine.setAudioAttributes(audioAttributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.setListener(
|
engine.setListener(
|
||||||
onStart = { onStartListener?.invoke() },
|
onStart = { onStartListener?.invoke() },
|
||||||
|
|||||||
+1
-3
@@ -207,6 +207,4 @@ internal fun Iterable<Float>.normalize(
|
|||||||
return values.map { scale * ((it - currentMin) / range) + min }
|
return values.map { scale * ((it - currentMin) / range) + min }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.safeDiv(value: Int): Float {
|
private fun Int.safeDiv(value: Int): Float = if (value == 0) 0F else this / value.toFloat()
|
||||||
return if (value == 0) return 0F else this / value.toFloat()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ fun LoadOrCreateNote(
|
|||||||
content: @Composable (Note?) -> Unit,
|
content: @Composable (Note?) -> Unit,
|
||||||
) {
|
) {
|
||||||
var note by
|
var note by
|
||||||
remember(event.id) { mutableStateOf<Note?>(accountViewModel.getNoteIfExists(event.id)) }
|
remember(event.id) { mutableStateOf(accountViewModel.getNoteIfExists(event.id)) }
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
LaunchedEffect(key1 = event.id) {
|
LaunchedEffect(key1 = event.id) {
|
||||||
|
|||||||
+4
-4
@@ -49,7 +49,7 @@ class ChannelFeedContentState(
|
|||||||
val feedContent = _feedContent.asStateFlow()
|
val feedContent = _feedContent.asStateFlow()
|
||||||
|
|
||||||
// Simple counter that changes when it needs to invalidate everything
|
// Simple counter that changes when it needs to invalidate everything
|
||||||
private val _scrollToTop = MutableStateFlow<Int>(0)
|
private val _scrollToTop = MutableStateFlow(0)
|
||||||
val scrollToTop = _scrollToTop.asStateFlow()
|
val scrollToTop = _scrollToTop.asStateFlow()
|
||||||
var scrolltoTopPending = false
|
var scrolltoTopPending = false
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class ChannelFeedContentState(
|
|||||||
viewModelScope.launch(Dispatchers.IO) { _scrollToTop.emit(_scrollToTop.value + 1) }
|
viewModelScope.launch(Dispatchers.IO) { _scrollToTop.emit(_scrollToTop.value + 1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sentToTop() {
|
fun sentToTop() {
|
||||||
scrolltoTopPending = false
|
scrolltoTopPending = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,10 +98,10 @@ class ChannelFeedContentState(
|
|||||||
if (notes.isEmpty()) {
|
if (notes.isEmpty()) {
|
||||||
_feedContent.tryEmit(ChannelFeedState.Empty)
|
_feedContent.tryEmit(ChannelFeedState.Empty)
|
||||||
} else if (currentState is ChannelFeedState.Loaded) {
|
} else if (currentState is ChannelFeedState.Loaded) {
|
||||||
currentState.feed.tryEmit(LoadedFeedState<Channel>(notes, localFilter.showHiddenKey()))
|
currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey()))
|
||||||
} else {
|
} else {
|
||||||
_feedContent.tryEmit(
|
_feedContent.tryEmit(
|
||||||
ChannelFeedState.Loaded(MutableStateFlow(LoadedFeedState<Channel>(notes, localFilter.showHiddenKey()))),
|
ChannelFeedState.Loaded(MutableStateFlow(LoadedFeedState(notes, localFilter.showHiddenKey()))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.myapplication
|
package com.vitorpamplona.amethyst.ui.layouts
|
||||||
|
|
||||||
import androidx.compose.animation.core.AnimationSpec
|
import androidx.compose.animation.core.AnimationSpec
|
||||||
import androidx.compose.animation.core.AnimationState
|
import androidx.compose.animation.core.AnimationState
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.myapplication
|
package com.vitorpamplona.amethyst.ui.layouts
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||||
import com.vitorpamplona.myapplication.DisappearingBottomBar
|
|
||||||
import com.vitorpamplona.myapplication.DisappearingFloatingButton
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+1
-1
@@ -130,7 +130,7 @@ fun DisplayAccount(
|
|||||||
accountSessionManager: AccountSessionManager,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
var baseUser by remember(acc) {
|
var baseUser by remember(acc) {
|
||||||
mutableStateOf<User?>(
|
mutableStateOf(
|
||||||
decodePublicKeyAsHexOrNull(acc.npub)?.let {
|
decodePublicKeyAsHexOrNull(acc.npub)?.let {
|
||||||
LocalCache.getUserIfExists(it)
|
LocalCache.getUserIfExists(it)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class Nav(
|
|||||||
) {
|
) {
|
||||||
navigationScope.launch {
|
navigationScope.launch {
|
||||||
controller.navigate(route) {
|
controller.navigate(route) {
|
||||||
popUpTo<T>(klass) { inclusive = true }
|
popUpTo(klass) { inclusive = true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -323,7 +323,7 @@ suspend fun routeEditDraftTo(
|
|||||||
is ChatroomKeyable -> {
|
is ChatroomKeyable -> {
|
||||||
val room = draft.chatroomKey(account.userProfile().pubkeyHex)
|
val room = draft.chatroomKey(account.userProfile().pubkeyHex)
|
||||||
account.chatroomList.getOrCreatePrivateChatroom(room)
|
account.chatroomList.getOrCreatePrivateChatroom(room)
|
||||||
return Route.Room(room, draftId = note.idHex)
|
Route.Room(room, draftId = note.idHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
is TextNoteEvent -> {
|
is TextNoteEvent -> {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ fun HiddenNotePreview() {
|
|||||||
ThemeComparisonColumn(
|
ThemeComparisonColumn(
|
||||||
toPreview = {
|
toPreview = {
|
||||||
HiddenNote(
|
HiddenNote(
|
||||||
reports = persistentSetOf<Note>(),
|
reports = persistentSetOf(),
|
||||||
isHiddenAuthor = true,
|
isHiddenAuthor = true,
|
||||||
accountViewModel = mockAccountViewModel(),
|
accountViewModel = mockAccountViewModel(),
|
||||||
nav = EmptyNav(),
|
nav = EmptyNav(),
|
||||||
|
|||||||
@@ -481,7 +481,7 @@ private fun ReactionDetailGallery(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) {
|
) {
|
||||||
val defaultBackgroundColor = MaterialTheme.colorScheme.background
|
val defaultBackgroundColor = MaterialTheme.colorScheme.background
|
||||||
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
|
val backgroundColor = remember { mutableStateOf(defaultBackgroundColor) }
|
||||||
|
|
||||||
val hasReactions by observeNoteReferences(baseNote, accountViewModel)
|
val hasReactions by observeNoteReferences(baseNote, accountViewModel)
|
||||||
|
|
||||||
@@ -757,7 +757,7 @@ private fun SlidingAnimationCount(
|
|||||||
if (accountViewModel.settings.isPerformanceMode()) {
|
if (accountViewModel.settings.isPerformanceMode()) {
|
||||||
TextCount(baseCount, textColor)
|
TextCount(baseCount, textColor)
|
||||||
} else {
|
} else {
|
||||||
AnimatedContent<Int>(
|
AnimatedContent(
|
||||||
targetState = baseCount,
|
targetState = baseCount,
|
||||||
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
||||||
label = "SlidingAnimationCount",
|
label = "SlidingAnimationCount",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import kotlin.math.round
|
|||||||
private const val YEAR_DATE_FORMAT = "MMM dd, yyyy"
|
private const val YEAR_DATE_FORMAT = "MMM dd, yyyy"
|
||||||
private const val MONTH_DATE_FORMAT = "MMM dd"
|
private const val MONTH_DATE_FORMAT = "MMM dd"
|
||||||
|
|
||||||
var locale = Locale.getDefault()
|
var locale: Locale = Locale.getDefault()
|
||||||
var yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
var yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||||
var monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
var monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ class PollNoteViewModel : ViewModel() {
|
|||||||
valueMinimum?.let { minimum ->
|
valueMinimum?.let { minimum ->
|
||||||
valueMaximum?.let { maximum ->
|
valueMaximum?.let { maximum ->
|
||||||
if (minimum != maximum) {
|
if (minimum != maximum) {
|
||||||
options.add(((minimum + maximum) / 2).toLong())
|
options.add(((minimum + maximum) / 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -34,9 +34,9 @@ import kotlin.uuid.Uuid
|
|||||||
@Stable
|
@Stable
|
||||||
class DraftTagState {
|
class DraftTagState {
|
||||||
var current: String by mutableStateOf(newTag())
|
var current: String by mutableStateOf(newTag())
|
||||||
var usedDraftTags by mutableStateOf(setOf<String>(current))
|
var usedDraftTags by mutableStateOf(setOf(current))
|
||||||
|
|
||||||
private val _versions = MutableStateFlow<Int>(0)
|
private val _versions = MutableStateFlow(0)
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
val versions = _versions.debounce(1000)
|
val versions = _versions.debounce(1000)
|
||||||
|
|||||||
+1
-1
@@ -43,7 +43,7 @@ class UserSuggestionState(
|
|||||||
val account: Account,
|
val account: Account,
|
||||||
val requireAtSymbol: Boolean = true,
|
val requireAtSymbol: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val invalidations = MutableStateFlow<Int>(0)
|
val invalidations = MutableStateFlow(0)
|
||||||
val currentWord = MutableStateFlow("")
|
val currentWord = MutableStateFlow("")
|
||||||
val searchDataSourceState = SearchQueryState(MutableStateFlow(""), account)
|
val searchDataSourceState = SearchQueryState(MutableStateFlow(""), account)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ fun DisplayZapSplits(
|
|||||||
remember(noteEvent) {
|
remember(noteEvent) {
|
||||||
val list = noteEvent.zapSplitSetup()
|
val list = noteEvent.zapSplitSetup()
|
||||||
if (list.isEmpty() && useAuthorIfEmpty) {
|
if (list.isEmpty() && useAuthorIfEmpty) {
|
||||||
listOf<ZapSplitSetup>(
|
listOf(
|
||||||
ZapSplitSetup(
|
ZapSplitSetup(
|
||||||
pubKeyHex = noteEvent.pubKey,
|
pubKeyHex = noteEvent.pubKey,
|
||||||
relay = null,
|
relay = null,
|
||||||
|
|||||||
@@ -116,13 +116,13 @@ private fun RenderPledgeAmount(
|
|||||||
) {
|
) {
|
||||||
val repliesState by observeNoteReplies(baseNote, accountViewModel)
|
val repliesState by observeNoteReplies(baseNote, accountViewModel)
|
||||||
var reward by remember {
|
var reward by remember {
|
||||||
mutableStateOf<String>(
|
mutableStateOf(
|
||||||
showAmount(baseReward.amount),
|
showAmount(baseReward.amount),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasPledge by remember {
|
var hasPledge by remember {
|
||||||
mutableStateOf<Boolean>(
|
mutableStateOf(
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ fun DisplayUncitedHashtags(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val unusedHashtags by
|
val unusedHashtags by
|
||||||
produceState(initialValue = emptyList<String>()) {
|
produceState(initialValue = emptyList()) {
|
||||||
val tagsInEvent = event.hashtags()
|
val tagsInEvent = event.hashtags()
|
||||||
if (tagsInEvent.isNotEmpty()) {
|
if (tagsInEvent.isNotEmpty()) {
|
||||||
val state = CachedRichTextParser.parseText(content, event.tags.toImmutableListOfLists(), callbackUri)
|
val state = CachedRichTextParser.parseText(content, event.tags.toImmutableListOfLists(), callbackUri)
|
||||||
|
|||||||
+2
-2
@@ -474,7 +474,7 @@ open class CommentPostViewModel :
|
|||||||
myMultiOrchestrator.upload(
|
myMultiOrchestrator.upload(
|
||||||
alt,
|
alt,
|
||||||
contentWarningReason,
|
contentWarningReason,
|
||||||
MediaCompressor.Companion.intToCompressorQuality(mediaQuality),
|
MediaCompressor.intToCompressorQuality(mediaQuality),
|
||||||
server,
|
server,
|
||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
@@ -703,5 +703,5 @@ open class CommentPostViewModel :
|
|||||||
return location!!
|
return location!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun locationManager(): LocationState = Amethyst.Companion.instance.locationManager
|
override fun locationManager(): LocationState = Amethyst.instance.locationManager
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
|
|||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.taggedEmojis
|
import com.vitorpamplona.quartz.nip30CustomEmoji.taggedEmojis
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
public fun RenderEmojiPack(
|
fun RenderEmojiPack(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
actionable: Boolean,
|
actionable: Boolean,
|
||||||
backgroundColor: MutableState<Color>,
|
backgroundColor: MutableState<Color>,
|
||||||
@@ -86,7 +86,7 @@ public fun RenderEmojiPack(
|
|||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
public fun RenderEmojiPack(
|
fun RenderEmojiPack(
|
||||||
noteEvent: EmojiPackEvent,
|
noteEvent: EmojiPackEvent,
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
actionable: Boolean,
|
actionable: Boolean,
|
||||||
|
|||||||
@@ -649,12 +649,12 @@ fun RenderPollGameResultsPreview() {
|
|||||||
Column(Modifier.padding(10.dp)) {
|
Column(Modifier.padding(10.dp)) {
|
||||||
RenderPoll(
|
RenderPoll(
|
||||||
note,
|
note,
|
||||||
false,
|
makeItShort = false,
|
||||||
true,
|
canPreview = true,
|
||||||
2,
|
quotesLeft = 2,
|
||||||
remember { mutableStateOf(Color.Transparent) },
|
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||||
mockAccountViewModel(),
|
accountViewModel = mockAccountViewModel(),
|
||||||
EmptyNav(),
|
nav = EmptyNav(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -758,12 +758,12 @@ fun RenderPollColorResultsPreview() {
|
|||||||
Column(Modifier.padding(10.dp)) {
|
Column(Modifier.padding(10.dp)) {
|
||||||
RenderPoll(
|
RenderPoll(
|
||||||
note,
|
note,
|
||||||
false,
|
makeItShort = false,
|
||||||
true,
|
canPreview = true,
|
||||||
2,
|
quotesLeft = 2,
|
||||||
remember { mutableStateOf(Color.Transparent) },
|
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||||
mockAccountViewModel(),
|
accountViewModel = mockAccountViewModel(),
|
||||||
EmptyNav(),
|
nav = EmptyNav(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.RenderUserAsClickableText
|
import com.vitorpamplona.amethyst.ui.components.RenderUserAsClickableText
|
||||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||||
@@ -102,7 +101,7 @@ fun DisplayUncitedUsers(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
@Suppress("ProduceStateDoesNotAssignValue")
|
@Suppress("ProduceStateDoesNotAssignValue")
|
||||||
val uncitedUsers by produceState(initialValue = emptyList<User>()) {
|
val uncitedUsers by produceState(initialValue = emptyList()) {
|
||||||
val users = event.groupKeySetWithoutOwner() - event.citedUsers()
|
val users = event.groupKeySetWithoutOwner() - event.citedUsers()
|
||||||
if (users.isNotEmpty()) {
|
if (users.isNotEmpty()) {
|
||||||
val newUsers = accountViewModel.loadUsersSync(users.toList())
|
val newUsers = accountViewModel.loadUsersSync(users.toList())
|
||||||
|
|||||||
@@ -139,12 +139,12 @@ fun TorrentCommentPreview() {
|
|||||||
toPreview = {
|
toPreview = {
|
||||||
RenderTorrentComment(
|
RenderTorrentComment(
|
||||||
comment,
|
comment,
|
||||||
false,
|
makeItShort = false,
|
||||||
true,
|
canPreview = true,
|
||||||
3,
|
quotesLeft = 3,
|
||||||
ReplyRenderType.FULL,
|
unPackReply = ReplyRenderType.FULL,
|
||||||
remember { mutableStateOf(Color.Transparent) },
|
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||||
EmptyState,
|
editState = EmptyState,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ class AccountSessionManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun requestLoginUI() = _accountContent.update { AccountState.LoggedOff }
|
private fun requestLoginUI() = _accountContent.update { AccountState.LoggedOff }
|
||||||
|
|
||||||
suspend fun loginAndStartUI(
|
suspend fun loginAndStartUI(
|
||||||
key: String,
|
key: String,
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ class CompositionObserver(
|
|||||||
private val vmStore: StoreOwnerRegistry,
|
private val vmStore: StoreOwnerRegistry,
|
||||||
private val key: Any,
|
private val key: Any,
|
||||||
) : RememberObserver {
|
) : RememberObserver {
|
||||||
override fun onRemembered() {}
|
override fun onRemembered() {
|
||||||
|
// No action needed when remembered — registration happens at construction time.
|
||||||
|
}
|
||||||
|
|
||||||
override fun onForgotten() = vmStore.composableDetached(key)
|
override fun onForgotten() = vmStore.composableDetached(key)
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -357,10 +357,10 @@ class AccountViewModel(
|
|||||||
|
|
||||||
return if (isPostHidden || isDecryptedPostHidden) {
|
return if (isPostHidden || isDecryptedPostHidden) {
|
||||||
// Spam + Blocked Users + Hidden Words + Sensitive Content
|
// Spam + Blocked Users + Hidden Words + Sensitive Content
|
||||||
NoteComposeReportState(isPostHidden, false, false, isHiddenAuthor)
|
NoteComposeReportState(isPostHidden, isAcceptable = false, canPreview = false, isHiddenAuthor = isHiddenAuthor)
|
||||||
} else if (isFromLoggedIn || isFromLoggedInFollow) {
|
} else if (isFromLoggedIn || isFromLoggedInFollow) {
|
||||||
// No need to process if from trusted people
|
// No need to process if from trusted people
|
||||||
NoteComposeReportState(isPostHidden, true, true, isHiddenAuthor)
|
NoteComposeReportState(isPostHidden, isAcceptable = true, canPreview = true, isHiddenAuthor = isHiddenAuthor)
|
||||||
} else {
|
} else {
|
||||||
val newCanPreview = !note.hasAnyReports()
|
val newCanPreview = !note.hasAnyReports()
|
||||||
|
|
||||||
@@ -368,7 +368,7 @@ class AccountViewModel(
|
|||||||
|
|
||||||
if (newCanPreview && newIsAcceptable) {
|
if (newCanPreview && newIsAcceptable) {
|
||||||
// No need to process reports if nothing is wrong
|
// No need to process reports if nothing is wrong
|
||||||
NoteComposeReportState(isPostHidden, true, true, false)
|
NoteComposeReportState(isPostHidden, isAcceptable = true, canPreview = true, isHiddenAuthor = false)
|
||||||
} else {
|
} else {
|
||||||
NoteComposeReportState(
|
NoteComposeReportState(
|
||||||
isPostHidden,
|
isPostHidden,
|
||||||
@@ -511,7 +511,7 @@ class AccountViewModel(
|
|||||||
}.toMutableMap()
|
}.toMutableMap()
|
||||||
|
|
||||||
val results =
|
val results =
|
||||||
mapNotNullAsync<CombinedZap, DecryptedInfo>(
|
mapNotNullAsync(
|
||||||
zaps.filter { (it.request.event as? LnZapRequestEvent)?.isPrivateZap() == true },
|
zaps.filter { (it.request.event as? LnZapRequestEvent)?.isPrivateZap() == true },
|
||||||
) { next ->
|
) { next ->
|
||||||
val info = innerDecryptAmountMessage(next.request, next.response)
|
val info = innerDecryptAmountMessage(next.request, next.response)
|
||||||
@@ -1609,7 +1609,7 @@ class AccountViewModel(
|
|||||||
}.stateIn(
|
}.stateIn(
|
||||||
viewModelScope,
|
viewModelScope,
|
||||||
SharingStarted.WhileSubscribed(5000),
|
SharingStarted.WhileSubscribed(5000),
|
||||||
emptySet<HexKey>(),
|
emptySet(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val draftNoteCache = CachedDraftNotes(this)
|
val draftNoteCache = CachedDraftNotes(this)
|
||||||
|
|||||||
+1
-1
@@ -173,7 +173,7 @@ fun ChatBubbleLayout(
|
|||||||
private fun BubblePreview() {
|
private fun BubblePreview() {
|
||||||
val bgColor =
|
val bgColor =
|
||||||
remember {
|
remember {
|
||||||
mutableStateOf<Color>(Color.Transparent)
|
mutableStateOf(Color.Transparent)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
+2
-2
@@ -222,8 +222,8 @@ fun RenderRelayLinePreview() {
|
|||||||
"wss://nos.lol",
|
"wss://nos.lol",
|
||||||
"http://icon.com/icon.ico",
|
"http://icon.com/icon.ico",
|
||||||
Modifier,
|
Modifier,
|
||||||
true,
|
showPicture = true,
|
||||||
true,
|
loadRobohash = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -73,14 +73,14 @@ fun NewChatroomSubjectDialog(
|
|||||||
Surface {
|
Surface {
|
||||||
val groupName =
|
val groupName =
|
||||||
remember {
|
remember {
|
||||||
mutableStateOf<String>(
|
mutableStateOf(
|
||||||
accountViewModel.account.chatroomList.rooms
|
accountViewModel.account.chatroomList.rooms
|
||||||
.get(room)
|
.get(room)
|
||||||
?.subject
|
?.subject
|
||||||
?.value ?: "",
|
?.value ?: "",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val message = remember { mutableStateOf<String>("") }
|
val message = remember { mutableStateOf("") }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
|||||||
fun filterMessagesToPublicChat(
|
fun filterMessagesToPublicChat(
|
||||||
channel: PublicChatChannel,
|
channel: PublicChatChannel,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? =
|
): List<RelayBasedFilter> =
|
||||||
channel.relays().toSet().map {
|
channel.relays().toSet().map {
|
||||||
RelayBasedFilter(
|
RelayBasedFilter(
|
||||||
relay = it,
|
relay = it,
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ fun filterMyMessagesToLiveActivities(
|
|||||||
channel: LiveActivitiesChannel,
|
channel: LiveActivitiesChannel,
|
||||||
pubKey: HexKey,
|
pubKey: HexKey,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? =
|
): List<RelayBasedFilter> =
|
||||||
channel.relays().toSet().map {
|
channel.relays().toSet().map {
|
||||||
RelayBasedFilter(
|
RelayBasedFilter(
|
||||||
relay = it,
|
relay = it,
|
||||||
|
|||||||
+1
-1
@@ -129,7 +129,7 @@ class ChannelMetadataViewModel : ViewModel() {
|
|||||||
|
|
||||||
val template =
|
val template =
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
val hint = EventHintBundle<ChannelCreateEvent>(event, channel.relays().firstOrNull())
|
val hint = EventHintBundle(event, channel.relays().firstOrNull())
|
||||||
|
|
||||||
ChannelMetadataEvent.build(
|
ChannelMetadataEvent.build(
|
||||||
channelName.value.text,
|
channelName.value.text,
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ class FollowingEphemeralChatSubAssembler(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: ChatroomListState,
|
key: ChatroomListState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? =
|
): List<RelayBasedFilter> =
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
filterFollowingEphemeralChats(key.account.ephemeralChatList.liveEphemeralChatList.value, since),
|
filterFollowingEphemeralChats(key.account.ephemeralChatList.liveEphemeralChatList.value, since),
|
||||||
).flatten()
|
).flatten()
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ class FollowingPublicChatSubAssembler(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: ChatroomListState,
|
key: ChatroomListState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? =
|
): List<RelayBasedFilter> =
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
filterLastMessageFollowingPublicChats(key.account.publicChatList.flowSet.value, since),
|
filterLastMessageFollowingPublicChats(key.account.publicChatList.flowSet.value, since),
|
||||||
filterFollowingPublicChatsCreationEvent(key.account.publicChatList.flowSet.value, since),
|
filterFollowingPublicChatsCreationEvent(key.account.publicChatList.flowSet.value, since),
|
||||||
|
|||||||
-1
@@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
|||||||
-1
@@ -42,7 +42,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
|||||||
+1
-1
@@ -238,7 +238,7 @@ fun LoadParticipants(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val hostsAuthor = hosts + (baseNote.author?.let { listOf(it) } ?: emptyList<User>())
|
val hostsAuthor = hosts + (baseNote.author?.let { listOf(it) } ?: emptyList())
|
||||||
|
|
||||||
val topFilter = accountViewModel.account.liveDiscoveryFollowLists.value
|
val topFilter = accountViewModel.account.liveDiscoveryFollowLists.value
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -189,7 +189,7 @@ fun RenderContentDVMThumb(
|
|||||||
card.personalized?.let {
|
card.personalized?.let {
|
||||||
var color = Color.DarkGray
|
var color = Color.DarkGray
|
||||||
var name = "generic"
|
var name = "generic"
|
||||||
if (card.personalized == true) {
|
if (card.personalized) {
|
||||||
color = MaterialTheme.colorScheme.bitcoinColor
|
color = MaterialTheme.colorScheme.bitcoinColor
|
||||||
name = "Personalized"
|
name = "Personalized"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-1
@@ -142,7 +142,7 @@ open class NewProductViewModel :
|
|||||||
var price by mutableStateOf(TextFieldValue(""))
|
var price by mutableStateOf(TextFieldValue(""))
|
||||||
var locationText by mutableStateOf(TextFieldValue(""))
|
var locationText by mutableStateOf(TextFieldValue(""))
|
||||||
var category by mutableStateOf(TextFieldValue(""))
|
var category by mutableStateOf(TextFieldValue(""))
|
||||||
var condition by mutableStateOf<ConditionTag.CONDITION>(ConditionTag.CONDITION.USED_LIKE_NEW)
|
var condition by mutableStateOf(ConditionTag.CONDITION.USED_LIKE_NEW)
|
||||||
|
|
||||||
// Invoices
|
// Invoices
|
||||||
var canAddInvoice by mutableStateOf(false)
|
var canAddInvoice by mutableStateOf(false)
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ class GeoHashFeedFilter(
|
|||||||
|
|
||||||
override fun applyFilter(newItems: Set<Note>): Set<Note> = innerApplyFilter(newItems)
|
override fun applyFilter(newItems: Set<Note>): Set<Note> = innerApplyFilter(newItems)
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet()) { acceptableEvent(it, tag) }
|
||||||
|
|
||||||
fun acceptableEvent(
|
fun acceptableEvent(
|
||||||
it: Note,
|
it: Note,
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class GeoHashFeedFilterSubAssembler(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: GeohashQueryState,
|
key: GeohashQueryState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? = filterPostsByGeohash(key.geohash, key.relays, since)
|
): List<RelayBasedFilter> = filterPostsByGeohash(key.geohash, key.relays, since)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only one key per hashtag.
|
* Only one key per hashtag.
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ class HashtagFeedFilter(
|
|||||||
|
|
||||||
override fun applyFilter(newItems: Set<Note>): Set<Note> = innerApplyFilter(newItems)
|
override fun applyFilter(newItems: Set<Note>): Set<Note> = innerApplyFilter(newItems)
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet()) { acceptableEvent(it, tag) }
|
||||||
|
|
||||||
fun acceptableEvent(
|
fun acceptableEvent(
|
||||||
it: Note,
|
it: Note,
|
||||||
|
|||||||
+2
-1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -220,7 +221,7 @@ open class ShortNotePostViewModel :
|
|||||||
var canUsePoll by mutableStateOf(false)
|
var canUsePoll by mutableStateOf(false)
|
||||||
var wantsPoll by mutableStateOf(false)
|
var wantsPoll by mutableStateOf(false)
|
||||||
var pollOptions: SnapshotStateMap<Int, OptionTag> = newStateMapPollOptions()
|
var pollOptions: SnapshotStateMap<Int, OptionTag> = newStateMapPollOptions()
|
||||||
var closedAt by mutableStateOf(TimeUtils.oneDayAhead())
|
var closedAt by mutableLongStateOf(TimeUtils.oneDayAhead())
|
||||||
|
|
||||||
// Invoices
|
// Invoices
|
||||||
var canAddInvoice by mutableStateOf(false)
|
var canAddInvoice by mutableStateOf(false)
|
||||||
|
|||||||
+1
-1
@@ -194,7 +194,7 @@ class HomeLiveFilter(
|
|||||||
|
|
||||||
return collection.sortedWith(
|
return collection.sortedWith(
|
||||||
compareByDescending<Channel> { followCounts[it] }
|
compareByDescending<Channel> { followCounts[it] }
|
||||||
.thenByDescending<Channel> { it.lastNote?.createdAt() ?: 0L }
|
.thenByDescending { it.lastNote?.createdAt() ?: 0L }
|
||||||
.thenBy { it.hashCode() },
|
.thenBy { it.hashCode() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -426,7 +426,7 @@ private fun ListModifyDescriptionDialog(
|
|||||||
onDismissDialog: () -> Unit,
|
onDismissDialog: () -> Unit,
|
||||||
onModifyDescription: (String) -> Unit,
|
onModifyDescription: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val updatedDescription = remember { mutableStateOf<String>("") }
|
val updatedDescription = remember { mutableStateOf("") }
|
||||||
|
|
||||||
val modifyIndicatorLabel =
|
val modifyIndicatorLabel =
|
||||||
if (currentDescription == null) {
|
if (currentDescription == null) {
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ class CardFeedContentState(
|
|||||||
val feedContent = _feedContent.asStateFlow()
|
val feedContent = _feedContent.asStateFlow()
|
||||||
|
|
||||||
// Simple counter that changes when it needs to invalidate everything
|
// Simple counter that changes when it needs to invalidate everything
|
||||||
private val _scrollToTop = MutableStateFlow<Int>(0)
|
private val _scrollToTop = MutableStateFlow(0)
|
||||||
val scrollToTop = _scrollToTop.asStateFlow()
|
val scrollToTop = _scrollToTop.asStateFlow()
|
||||||
var scrolltoTopPending = false
|
var scrolltoTopPending = false
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -82,9 +82,9 @@ class NotificationSummaryState(
|
|||||||
Instant.ofEpochSecond(createAt).atZone(ZoneId.systemDefault()).toLocalDateTime(),
|
Instant.ofEpochSecond(createAt).atZone(ZoneId.systemDefault()).toLocalDateTime(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun today() = sdf.format(LocalDateTime.now())
|
fun today(): String = sdf.format(LocalDateTime.now())
|
||||||
|
|
||||||
public suspend fun initializeSuspend() {
|
suspend fun initializeSuspend() {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val currentUser = user.pubkeyHex
|
val currentUser = user.pubkeyHex
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class UserProfileFollowersFilterSubAssembler(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
key: UserProfileQueryState,
|
key: UserProfileQueryState,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? = filterUserProfileFollowers(user(key), since)
|
): List<RelayBasedFilter> = filterUserProfileFollowers(user(key), since)
|
||||||
|
|
||||||
override fun user(key: UserProfileQueryState) = key.user
|
override fun user(key: UserProfileQueryState) = key.user
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ class UserProfileMetadataFilterSubAssembler(
|
|||||||
override fun updateFilter(
|
override fun updateFilter(
|
||||||
keys: List<UserProfileQueryState>,
|
keys: List<UserProfileQueryState>,
|
||||||
since: SincePerRelayMap?,
|
since: SincePerRelayMap?,
|
||||||
): List<RelayBasedFilter>? {
|
): List<RelayBasedFilter> {
|
||||||
val userPerRelay =
|
val userPerRelay =
|
||||||
mapOfSet {
|
mapOfSet {
|
||||||
keys.mapTo(mutableSetOf()) { key -> key.user }.forEach { user ->
|
keys.mapTo(mutableSetOf()) { key -> key.user }.forEach { user ->
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ class UserProfileReportsFeedFilter(
|
|||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> =
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> =
|
||||||
collection
|
collection
|
||||||
.filterTo(mutableSetOf<Note>()) {
|
.filterTo(mutableSetOf()) {
|
||||||
it.event is ReportEvent && it.event?.isTaggedUser(user.pubkeyHex) == true
|
it.event is ReportEvent && it.event?.isTaggedUser(user.pubkeyHex) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class BlockedRelayListViewModel : BasicRelaySetupInfoModel() {
|
class BlockedRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||||
override fun getRelayList(): List<NormalizedRelayUrl>? =
|
override fun getRelayList(): List<NormalizedRelayUrl> =
|
||||||
account.blockedRelayList.flow.value
|
account.blockedRelayList.flow.value
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class BroadcastRelayListViewModel : BasicRelaySetupInfoModel() {
|
class BroadcastRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||||
override fun getRelayList(): List<NormalizedRelayUrl>? =
|
override fun getRelayList(): List<NormalizedRelayUrl> =
|
||||||
account.broadcastRelayList.flow.value
|
account.broadcastRelayList.flow.value
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
|
|||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -48,7 +47,7 @@ class RelaySuggestionState {
|
|||||||
.sortedBy { it.url }
|
.sortedBy { it.url }
|
||||||
.take(20)
|
.take(20)
|
||||||
} else {
|
} else {
|
||||||
emptyList<NormalizedRelayUrl>()
|
emptyList()
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO)
|
}.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class FavoriteRelayListViewModel : BasicRelaySetupInfoModel() {
|
class FavoriteRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||||
override fun getRelayList(): List<NormalizedRelayUrl>? =
|
override fun getRelayList(): List<NormalizedRelayUrl> =
|
||||||
account.favoriteRelayList.flow.value
|
account.favoriteRelayList.flow.value
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class IndexerRelayListViewModel : BasicRelaySetupInfoModel() {
|
class IndexerRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||||
override fun getRelayList(): List<NormalizedRelayUrl>? =
|
override fun getRelayList(): List<NormalizedRelayUrl> =
|
||||||
account.indexerRelayList.flowNoDefaults.value
|
account.indexerRelayList.flowNoDefaults.value
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user