Merge pull request #1774 from davotoula/lint-fixes

Lint fixes
This commit is contained in:
Vitor Pamplona
2026-03-06 15:12:43 -05:00
committed by GitHub
110 changed files with 184 additions and 198 deletions
@@ -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>> {
@@ -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),
@@ -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()
} }
@@ -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>,
@@ -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(),
) )
@@ -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,
) )
@@ -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 ->
@@ -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 ->
@@ -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(
@@ -48,7 +48,7 @@ class OutboxLoaderState(
}.flowOn(Dispatchers.IO) }.flowOn(Dispatchers.IO)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Companion.Eagerly, SharingStarted.Eagerly,
UnknownTopNavPerRelayFilterSet, UnknownTopNavPerRelayFilterSet,
) )
} }
@@ -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
@@ -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
@@ -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) }
} }
@@ -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
@@ -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) }
} }
@@ -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)
@@ -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,
@@ -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>,
) { ) {
@@ -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) {
@@ -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)
} }
} }
@@ -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(),
) )
} }
@@ -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(
@@ -76,7 +76,7 @@ fun MuteButton(
) { ) {
val holdOn = val holdOn =
remember { remember {
mutableStateOf<Boolean>( mutableStateOf(
true, true,
) )
} }
@@ -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) }
@@ -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)
@@ -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 {
@@ -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
} }
@@ -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,
@@ -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) {
@@ -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,
@@ -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 {
@@ -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
} }
@@ -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 ->
@@ -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 ->
@@ -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,
@@ -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,
@@ -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(
@@ -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() },
@@ -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) {
@@ -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()))),
) )
} }
} }
@@ -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
@@ -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
@@ -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 }
} }
} }
} }
@@ -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))
} }
} }
} }
@@ -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)
@@ -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)
@@ -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,
) )
} }
@@ -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)
@@ -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,
) )
@@ -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)
@@ -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)
@@ -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 {
@@ -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,
) )
} }
} }
@@ -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(
@@ -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,
@@ -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,
@@ -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,
@@ -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()
@@ -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),
@@ -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
@@ -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
@@ -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
@@ -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 {
@@ -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)
@@ -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,
@@ -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.
@@ -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,
@@ -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)
@@ -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() },
) )
} }
@@ -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) {
@@ -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
@@ -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
@@ -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
} }
@@ -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 ->
@@ -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
} }
@@ -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()
@@ -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()
@@ -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)
@@ -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()
@@ -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