Merge branch 'main' of https://github.com/vitorpamplona/amethyst
* 'main' of https://github.com/vitorpamplona/amethyst: (35 commits) New Crowdin translations by GitHub Action fix chess tests Replace subsequent checks with '!isNullOrEmpty()' call use forEach: no intermediate list created. Use filterIsInstance to avoid dual iteration Lambda argument should be moved out of parentheses replace null checks with Elvis merge call chain with flatMap (slight performance improvement) New Crowdin translations by GitHub Action New Crowdin translations by GitHub Action update cz, pt, de, sv was this meant to be withContext? add names to boolean params remove unused imports Explicit type arguments can be inferred remove redundant modifiers correct package name If-Null return/break/... foldable to '?:' Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable. Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable. ... # Conflicts: # amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ReactionsSettingsScreen.kt
This commit is contained in:
@@ -80,7 +80,7 @@ class ImageUploadTesting {
|
||||
.addInterceptor(DefaultContentTypeInterceptor("Amethyst/${BuildConfig.VERSION_NAME}"))
|
||||
.build()
|
||||
|
||||
private suspend fun getBitmap(): ByteArray {
|
||||
private fun getBitmap(): ByteArray {
|
||||
val bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
|
||||
for (x in 0 until bitmap.width) {
|
||||
for (y in 0 until bitmap.height) {
|
||||
@@ -127,7 +127,7 @@ class ImageUploadTesting {
|
||||
// assertEquals(server.baseUrl, "${server.baseUrl}/$initialHash", result.url?.removeSuffix(".png"))
|
||||
|
||||
val imageData: ByteArray =
|
||||
ImageDownloader().waitAndGetImage(result.url!!, { client })?.bytes
|
||||
ImageDownloader().waitAndGetImage(result.url!!) { client }?.bytes
|
||||
?: run {
|
||||
fail("${server.name}: Should not be null")
|
||||
return
|
||||
@@ -142,8 +142,7 @@ class ImageUploadTesting {
|
||||
ServerInfoRetriever()
|
||||
.loadInfo(
|
||||
server.baseUrl,
|
||||
{ client },
|
||||
)
|
||||
) { client }
|
||||
|
||||
val payload = getBitmap()
|
||||
val inputStream = payload.inputStream()
|
||||
@@ -172,7 +171,7 @@ class ImageUploadTesting {
|
||||
Assert.assertTrue("${server.name}: Invalid result url", url.startsWith("http"))
|
||||
|
||||
val imageData: ByteArray =
|
||||
ImageDownloader().waitAndGetImage(url, { client })?.bytes
|
||||
ImageDownloader().waitAndGetImage(url) { client }?.bytes
|
||||
?: run {
|
||||
fail("${server.name}: Should not be null")
|
||||
return
|
||||
|
||||
@@ -147,12 +147,14 @@ class AppModules(
|
||||
// Custom fetcher that considers tor settings and avoids forwarding.
|
||||
val nip05Fetcher = OkHttpNip05Fetcher(roleBasedHttpClientBuilder::okHttpClientForNip05)
|
||||
|
||||
val namecoinElectrumxClient =
|
||||
ElectrumXClient(
|
||||
socketFactory = { roleBasedHttpClientBuilder.socketFactoryForNip05() },
|
||||
)
|
||||
|
||||
val namecoinResolver =
|
||||
NamecoinNameResolver(
|
||||
electrumxClient =
|
||||
ElectrumXClient(
|
||||
socketFactory = { roleBasedHttpClientBuilder.socketFactoryForNip05() },
|
||||
),
|
||||
electrumxClient = namecoinElectrumxClient,
|
||||
serverListProvider = {
|
||||
if (roleBasedHttpClientBuilder.shouldUseTorForNIP05("https://electrumx.example.com")) {
|
||||
TOR_ELECTRUMX_SERVERS
|
||||
@@ -226,7 +228,7 @@ class AppModules(
|
||||
val relayStats = RelayStats(client)
|
||||
|
||||
// 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 logger = if (isDebug) RelaySpeedLogger(client) else null
|
||||
|
||||
|
||||
@@ -1240,7 +1240,7 @@ class Account(
|
||||
val event = signer.sign(template)
|
||||
cache.justConsumeMyOwnEvent(event)
|
||||
val relays = relayList(event)
|
||||
if (relays != null && relays.isNotEmpty()) {
|
||||
if (!relays.isNullOrEmpty()) {
|
||||
client.send(event, relays.toSet())
|
||||
} else {
|
||||
client.send(event, computeRelayListToBroadcast(event))
|
||||
@@ -1898,7 +1898,7 @@ class Account(
|
||||
fun getRelevantReports(note: Note): Set<Note> {
|
||||
val innerReports =
|
||||
if (note.event is RepostEvent || note.event is GenericRepostEvent) {
|
||||
note.replyTo?.map { getRelevantReports(it) }?.flatten() ?: emptyList()
|
||||
note.replyTo?.flatMap { getRelevantReports(it) } ?: emptyList()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
@@ -186,8 +186,8 @@ class AccountSettings(
|
||||
var backupEphemeralChatList: EphemeralChatListEvent? = null,
|
||||
var backupTrustProviderList: TrustProviderListEvent? = null,
|
||||
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
||||
val hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf<String>()),
|
||||
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow<Map<HexKey, String>>(mapOf()),
|
||||
val hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf()),
|
||||
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow(mapOf()),
|
||||
var backupNipA3PaymentTargets: PaymentTargetsEvent? = null,
|
||||
) : EphemeralChatRepository,
|
||||
PublicChatListRepository {
|
||||
@@ -612,7 +612,7 @@ class AccountSettings(
|
||||
route: String,
|
||||
timestampInSecs: Long,
|
||||
): MutableStateFlow<Long> =
|
||||
MutableStateFlow<Long>(timestampInSecs).also { newFlow ->
|
||||
MutableStateFlow(timestampInSecs).also { newFlow ->
|
||||
lastReadPerRoute.update { it + Pair(route, newFlow) }
|
||||
saveAccountSettings()
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ class AntiSpamFilter {
|
||||
}
|
||||
}
|
||||
|
||||
val flowSpam = MutableStateFlow<AntiSpamState>(AntiSpamState(this))
|
||||
val flowSpam = MutableStateFlow(AntiSpamState(this))
|
||||
}
|
||||
|
||||
class AntiSpamState(
|
||||
|
||||
@@ -25,14 +25,13 @@ import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.ConcurrentSkipListMap
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
||||
protected val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
|
||||
class LargeSoftCache<K : Any, V : Any> : CacheOperations<K, V> {
|
||||
private val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
|
||||
|
||||
fun keys() = cache.keys
|
||||
|
||||
fun get(key: K): V? {
|
||||
val softRef = cache.get(key)
|
||||
if (softRef == null) return null
|
||||
val softRef = cache.get(key) ?: return null
|
||||
val value = softRef.get()
|
||||
|
||||
return if (value != null) {
|
||||
@@ -99,7 +98,7 @@ class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
||||
// another thread may put in between
|
||||
cache.remove(key, softRef)
|
||||
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))
|
||||
}
|
||||
|
||||
class BiConsumerWrapper<K, V>(
|
||||
class BiConsumerWrapper<K : Any, V : Any>(
|
||||
val cache: LargeSoftCache<K, V>,
|
||||
val inner: BiConsumer<K, V>,
|
||||
) : BiConsumer<K, WeakReference<V>> {
|
||||
|
||||
@@ -2770,10 +2770,9 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
val toBeRemoved =
|
||||
account.hiddenUsers.flow.value.hiddenUsers
|
||||
.map { userHex ->
|
||||
(notes.filter { _, it -> it.event?.pubKey == userHex } + addressables.filter { _, it -> it.event?.pubKey == userHex }).toSet()
|
||||
}.flatten()
|
||||
account.hiddenUsers.flow.value.hiddenUsers.flatMap { userHex ->
|
||||
(notes.filter { _, it -> it.event?.pubKey == userHex } + addressables.filter { _, it -> it.event?.pubKey == userHex }).toSet()
|
||||
}
|
||||
|
||||
toBeRemoved.forEach {
|
||||
removeFromCache(it)
|
||||
|
||||
+4
-4
@@ -34,9 +34,9 @@ class TorAwareOkHttpOtsResolverBuilder(
|
||||
) : OtsResolverBuilder {
|
||||
fun getAPI(usingTor: Boolean) =
|
||||
if (usingTor) {
|
||||
OkHttpBitcoinExplorer.Companion.MEMPOOL_API_URL
|
||||
OkHttpBitcoinExplorer.MEMPOOL_API_URL
|
||||
} else {
|
||||
OkHttpBitcoinExplorer.Companion.BLOCKSTREAM_API_URL
|
||||
OkHttpBitcoinExplorer.BLOCKSTREAM_API_URL
|
||||
}
|
||||
|
||||
override fun build(): OtsResolver =
|
||||
@@ -44,9 +44,9 @@ class TorAwareOkHttpOtsResolverBuilder(
|
||||
explorer =
|
||||
OkHttpBitcoinExplorer(
|
||||
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,
|
||||
),
|
||||
calendar = OkHttpCalendar(okHttpClient),
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ class BookmarkListState(
|
||||
|
||||
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
|
||||
return noteEvent?.publicBookmarks() ?: emptyList()
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class HiddenUsersState(
|
||||
) {
|
||||
var transientHiddenUsers: MutableStateFlow<Set<String>> = MutableStateFlow(setOf())
|
||||
|
||||
suspend fun assembleLiveHiddenUsers(
|
||||
fun assembleLiveHiddenUsers(
|
||||
blockList: List<MuteTag>,
|
||||
muteList: List<MuteTag>,
|
||||
transientHiddenUsers: Set<String>,
|
||||
|
||||
+4
-4
@@ -34,9 +34,9 @@ data class LabeledBookmarkList(
|
||||
val privateBookmarks: Set<BookmarkIdTag> = emptySet(),
|
||||
val publicBookmarks: Set<BookmarkIdTag> = emptySet(),
|
||||
) {
|
||||
val privatePostBookmarks = privateBookmarks.filter { it is EventBookmark }.map { bookmarkIdTag -> bookmarkIdTag as EventBookmark }
|
||||
val publicPostBookmarks = publicBookmarks.filter { it is EventBookmark }.map { bookmarkIdTag -> bookmarkIdTag as EventBookmark }
|
||||
val privatePostBookmarks = privateBookmarks.filterIsInstance<EventBookmark>()
|
||||
val publicPostBookmarks = publicBookmarks.filterIsInstance<EventBookmark>()
|
||||
|
||||
val privateArticleBookmarks = privateBookmarks.filter { it is AddressBookmark }.map { bookmarkIdTag -> bookmarkIdTag as AddressBookmark }
|
||||
val publicArticleBookmarks = publicBookmarks.filter { it is AddressBookmark }.map { bookmarkIdTag -> bookmarkIdTag as AddressBookmark }
|
||||
val privateArticleBookmarks = privateBookmarks.filterIsInstance<AddressBookmark>()
|
||||
val publicArticleBookmarks = publicBookmarks.filterIsInstance<AddressBookmark>()
|
||||
}
|
||||
|
||||
+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
|
||||
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
|
||||
|
||||
@@ -70,7 +70,7 @@ class SearchRelayListState(
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Companion.Eagerly,
|
||||
SharingStarted.Eagerly,
|
||||
emptySet(),
|
||||
)
|
||||
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ class TrustedRelayListState(
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Companion.Eagerly,
|
||||
SharingStarted.Eagerly,
|
||||
emptySet(),
|
||||
)
|
||||
|
||||
@@ -76,13 +76,13 @@ class TrustedRelayListState(
|
||||
val relayListForTrusted = getTrustedRelayList()
|
||||
|
||||
return if (relayListForTrusted != null && relayListForTrusted.tags.isNotEmpty()) {
|
||||
TrustedRelayListEvent.Companion.updateRelayList(
|
||||
TrustedRelayListEvent.updateRelayList(
|
||||
earlierVersion = relayListForTrusted,
|
||||
relays = trustedRelays,
|
||||
signer = signer,
|
||||
)
|
||||
} else {
|
||||
TrustedRelayListEvent.Companion.create(
|
||||
TrustedRelayListEvent.create(
|
||||
relays = trustedRelays,
|
||||
signer = signer,
|
||||
)
|
||||
|
||||
+1
-1
@@ -120,5 +120,5 @@ class BlossomServerListState(
|
||||
suspend fun createBlossomDeleteAuth(
|
||||
hash: HexKey,
|
||||
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 java.io.IOException
|
||||
|
||||
suspend fun <T> DataStore<Preferences>.getProperty(
|
||||
fun <T> DataStore<Preferences>.getProperty(
|
||||
key: Preferences.Key<String>,
|
||||
parser: (String) -> T,
|
||||
serializer: (T) -> String,
|
||||
scope: CoroutineScope,
|
||||
): UpdatablePropertyFlow<T> =
|
||||
UpdatablePropertyFlow<T>(
|
||||
UpdatablePropertyFlow(
|
||||
flow =
|
||||
data
|
||||
.catch { e ->
|
||||
|
||||
+2
-2
@@ -67,12 +67,12 @@ class EncryptedDataStore(
|
||||
?.get(key)
|
||||
?.let { decrypt(it) }
|
||||
|
||||
suspend fun <T> getProperty(
|
||||
fun <T> getProperty(
|
||||
key: Preferences.Key<String>,
|
||||
parser: (String) -> T,
|
||||
serializer: (T) -> String,
|
||||
): UpdatablePropertyFlow<T> =
|
||||
UpdatablePropertyFlow<T>(
|
||||
UpdatablePropertyFlow(
|
||||
flow =
|
||||
store.data
|
||||
.catch { e ->
|
||||
|
||||
+2
-2
@@ -60,8 +60,8 @@ class MergedFollowListsState(
|
||||
val geotags: Set<String> = emptySet(),
|
||||
val communities: Set<String> = emptySet(),
|
||||
) {
|
||||
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
||||
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
|
||||
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||
}
|
||||
|
||||
fun mergeLists(
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class OutboxLoaderState(
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Companion.Eagerly,
|
||||
SharingStarted.Eagerly,
|
||||
UnknownTopNavPerRelayFilterSet,
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -52,8 +52,8 @@ class AllFollowsByOutboxTopNavFilter(
|
||||
val defaultRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
) : IFeedTopNavFilter {
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.Companion.toScope(it) }
|
||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.Companion.toScope(it) }
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||
|
||||
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 proxyRelays: Set<NormalizedRelayUrl>,
|
||||
) : IFeedTopNavFilter {
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.Companion.toScope(it) }
|
||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.Companion.toScope(it) }
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.toScope(it) }
|
||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf()) { HashtagId.toScope(it) }
|
||||
|
||||
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 communities: Set<String>? = null,
|
||||
) : IFeedTopNavPerRelayFilter {
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
|
||||
val hashtagScopes: Set<String>? = hashtags?.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
|
||||
val geotagScopes: Set<String>? = geotags?.mapTo(mutableSetOf()) { GeohashId.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 relayList: Set<NormalizedRelayUrl>,
|
||||
) : 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
|
||||
|
||||
|
||||
+1
-1
@@ -28,5 +28,5 @@ import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
|
||||
class LocationTopNavPerRelayFilter(
|
||||
val geotags: Set<String>,
|
||||
) : 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 =
|
||||
when (noteEvent) {
|
||||
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)
|
||||
} else {
|
||||
AuthorsByOutboxTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), blockedRelays)
|
||||
@@ -109,7 +109,7 @@ class NoteFeedFlow(
|
||||
) {
|
||||
when (noteEvent) {
|
||||
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))
|
||||
} else {
|
||||
emit(AuthorsByOutboxTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), blockedRelays))
|
||||
@@ -161,7 +161,7 @@ class NoteFeedFlow(
|
||||
): IFeedTopNavFilter =
|
||||
when (noteEvent) {
|
||||
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)
|
||||
} else {
|
||||
AuthorsByProxyTopNavFilter(caches.peopleListCache.cachedUserIdSet(noteEvent), proxyRelays)
|
||||
@@ -208,7 +208,7 @@ class NoteFeedFlow(
|
||||
) {
|
||||
when (noteEvent) {
|
||||
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))
|
||||
} else {
|
||||
emit(AuthorsByProxyTopNavFilter(caches.peopleListCache.userIdSet(noteEvent), proxyRelays))
|
||||
@@ -272,7 +272,7 @@ class NoteFeedFlow(
|
||||
override fun startValue(): IFeedTopNavFilter {
|
||||
val noteEvent = metadataFlow.value?.note?.event
|
||||
return if (noteEvent == null) {
|
||||
return AuthorsByOutboxTopNavFilter(emptySet(), blockedRelays)
|
||||
AuthorsByOutboxTopNavFilter(emptySet(), blockedRelays)
|
||||
} else {
|
||||
if (proxyRelays.value.isEmpty()) {
|
||||
processByOutbox(noteEvent, outboxRelays.value)
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ class MeltProcessor {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun melt(
|
||||
fun melt(
|
||||
token: CashuToken,
|
||||
lud16: String,
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class MemoryTrimmingService(
|
||||
) {
|
||||
var isTrimmingMemoryMutex = AtomicBoolean(false)
|
||||
|
||||
private suspend fun doTrim(
|
||||
private fun doTrim(
|
||||
account: Collection<Account>,
|
||||
otherAccounts: List<AccountInfo>,
|
||||
) {
|
||||
|
||||
+4
-5
@@ -65,14 +65,11 @@ class LightningAddressResolver {
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
context: Context,
|
||||
): String {
|
||||
val url = assembleUrl(lnAddress)
|
||||
|
||||
if (url == null) {
|
||||
throw LightningAddressError(
|
||||
val url =
|
||||
assembleUrl(lnAddress) ?: throw LightningAddressError(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(context, R.string.could_not_assemble_lnurl_from_lightning_address_check_the_user_s_setup, lnAddress),
|
||||
)
|
||||
}
|
||||
|
||||
val client = okHttpClient(url)
|
||||
|
||||
@@ -126,12 +123,14 @@ class LightningAddressResolver {
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
context: Context,
|
||||
): String {
|
||||
@Suppress("BlockingMethodInNonBlockingContext") // URLEncoder.encode is CPU-only, not I/O blocking
|
||||
val encodedMessage = URLEncoder.encode(message, "utf-8")
|
||||
|
||||
val urlBinder = if (lnCallback.contains("?")) "&" else "?"
|
||||
var url = "$lnCallback${urlBinder}amount=$milliSats&comment=$encodedMessage"
|
||||
|
||||
if (nostrRequest != null) {
|
||||
@Suppress("BlockingMethodInNonBlockingContext") // URLEncoder.encode is CPU-only, not I/O blocking
|
||||
val encodedNostrRequest = URLEncoder.encode(nostrRequest.toJson(), "utf-8")
|
||||
url += "&nostr=$encodedNostrRequest"
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class LocationState(
|
||||
object Loading : LocationResult()
|
||||
}
|
||||
|
||||
private var hasLocationPermission = MutableStateFlow<Boolean>(false)
|
||||
private var hasLocationPermission = MutableStateFlow(false)
|
||||
private var latestLocation: LocationResult = LocationResult.Loading
|
||||
|
||||
fun setLocationPermission(newValue: Boolean) {
|
||||
|
||||
+13
-4
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.namecoin
|
||||
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.DEFAULT_ELECTRUMX_SERVERS
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumXClient
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinLookupCache
|
||||
@@ -43,12 +44,17 @@ class NamecoinNameService(
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
private val resolver = NamecoinNameResolver(electrumxClient)
|
||||
private val cache = NamecoinLookupCache()
|
||||
|
||||
// Custom server list (user-configurable)
|
||||
@Volatile
|
||||
private var customServers: List<ElectrumxServer> = emptyList()
|
||||
|
||||
private val resolver =
|
||||
NamecoinNameResolver(
|
||||
electrumxClient = electrumxClient,
|
||||
serverListProvider = { customServers.ifEmpty { DEFAULT_ELECTRUMX_SERVERS } },
|
||||
)
|
||||
private val cache = NamecoinLookupCache()
|
||||
|
||||
// ── Public API ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -97,7 +103,10 @@ class NamecoinNameService(
|
||||
*
|
||||
* Useful for composable UIs that observe resolution state.
|
||||
*/
|
||||
fun resolveLive(identifier: String): StateFlow<NamecoinResolveState> {
|
||||
fun resolveLive(
|
||||
identifier: String,
|
||||
scope: CoroutineScope = this.scope,
|
||||
): StateFlow<NamecoinResolveState> {
|
||||
val state = MutableStateFlow<NamecoinResolveState>(NamecoinResolveState.Loading)
|
||||
scope.launch {
|
||||
try {
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.service.okhttp
|
||||
import okhttp3.internal.concurrent.TaskRunner
|
||||
import okhttp3.internal.http2.Http2
|
||||
import java.io.Closeable
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Handler
|
||||
@@ -45,7 +46,7 @@ object OkHttpDebugLogging {
|
||||
level = Level.FINE
|
||||
formatter =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,7 @@ class OkHttpWebSocket(
|
||||
override fun needsReconnect(): Boolean {
|
||||
if (socket == null) return true
|
||||
|
||||
val myUsingOkHttp = usingOkHttp
|
||||
if (myUsingOkHttp == null) return true
|
||||
val myUsingOkHttp = usingOkHttp ?: return true
|
||||
|
||||
val currentOkHttp = httpClient(url)
|
||||
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ fun VideoView(
|
||||
) {
|
||||
val automaticallyStartPlayback =
|
||||
remember {
|
||||
mutableStateOf<Boolean>(
|
||||
mutableStateOf(
|
||||
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.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
public val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
||||
val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
||||
|
||||
@Composable
|
||||
fun VideoViewInner(
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ fun MuteButton(
|
||||
) {
|
||||
val holdOn =
|
||||
remember {
|
||||
mutableStateOf<Boolean>(
|
||||
mutableStateOf(
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ class IntentExtras {
|
||||
data.mimeType?.let { putString("mimeType", it) }
|
||||
data.aspectRatio?.let { putFloat("aspectRatio", 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()) }
|
||||
|
||||
bounds?.let { putInt("boundLeft", it.left) }
|
||||
|
||||
+3
-9
@@ -20,8 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.playback.pip
|
||||
|
||||
import android.content.Context.RECEIVER_EXPORTED
|
||||
import android.os.Build
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -34,6 +32,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.ui.compose.ContentFrame
|
||||
import androidx.media3.ui.compose.state.rememberMuteButtonState
|
||||
@@ -91,13 +90,8 @@ fun RegisterControllerReceiver(controllerState: MediaControllerState) {
|
||||
onPlayPause = controllerState::togglePlayPause,
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(receiver, receiver.filterMute, RECEIVER_EXPORTED)
|
||||
context.registerReceiver(receiver, receiver.filterPlayPause, RECEIVER_EXPORTED)
|
||||
} else {
|
||||
context.registerReceiver(receiver, receiver.filterMute)
|
||||
context.registerReceiver(receiver, receiver.filterPlayPause)
|
||||
}
|
||||
ContextCompat.registerReceiver(context, receiver, receiver.filterMute, ContextCompat.RECEIVER_EXPORTED)
|
||||
ContextCompat.registerReceiver(context, receiver, receiver.filterPlayPause, ContextCompat.RECEIVER_EXPORTED)
|
||||
|
||||
onDispose {
|
||||
context.unregisterReceiver(receiver)
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import okhttp3.OkHttpClient
|
||||
class ExoPlayerBuilder(
|
||||
val okHttp: OkHttpClient,
|
||||
) {
|
||||
fun build(context: Context) =
|
||||
fun build(context: Context): ExoPlayer =
|
||||
ExoPlayer
|
||||
.Builder(context)
|
||||
.apply {
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@ class ListWithUniqueSetCache<T, U>(
|
||||
val key: (T) -> U,
|
||||
) {
|
||||
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()
|
||||
|
||||
@@ -41,7 +41,7 @@ class ListWithUniqueSetCache<T, U>(
|
||||
}
|
||||
|
||||
fun distinct(): Set<U> {
|
||||
var currentSet = cacheSet.get()
|
||||
val currentSet = cacheSet.get()
|
||||
|
||||
// Check if the cached set is based on the current list
|
||||
if (currentSet != null) {
|
||||
@@ -49,7 +49,7 @@ class ListWithUniqueSetCache<T, U>(
|
||||
}
|
||||
|
||||
// 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)
|
||||
return newSet
|
||||
}
|
||||
|
||||
+1
-2
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
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.service.relayClient.reqCommand.account.AccountFilterAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.ChannelFinderFilterAssemblyGroup
|
||||
@@ -88,7 +87,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val nwc = NWCPaymentFilterAssembler(client)
|
||||
|
||||
val all =
|
||||
listOf<ComposeSubscriptionManagerControls>(
|
||||
listOf(
|
||||
account,
|
||||
home,
|
||||
chatroomList,
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ fun filterDraftsFromKey(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ class AccountFollowsLoaderSubAssembler(
|
||||
* 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).
|
||||
*/
|
||||
val hasTried: EOSEAccountFast<User> = EOSEAccountFast<User>(2000)
|
||||
val hasTried: EOSEAccountFast<User> = EOSEAccountFast(2000)
|
||||
|
||||
// updates all filters
|
||||
override fun invalidateFilters(ignoreIfDoing: Boolean) {
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ fun pickRelaysToLoadUsers(
|
||||
|
||||
val outbox = key.authorRelayList()?.writeRelaysNorm()
|
||||
|
||||
if (outbox != null && outbox.isNotEmpty()) {
|
||||
if (!outbox.isNullOrEmpty()) {
|
||||
// If there is a home, get from it.
|
||||
|
||||
// if it tried all outbox relays, stop.
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ fun filterBasicAccountInfoFromKeys(
|
||||
otherAccounts: List<HexKey>?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (otherAccounts == null || otherAccounts.isEmpty()) return emptyList()
|
||||
if (otherAccounts.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ fun filterBookmarksAndReportsFromKey(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class AccountNotificationsEoseFromInboxRelaysManager(
|
||||
override fun updateFilter(
|
||||
key: AccountQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? =
|
||||
): List<RelayBasedFilter> =
|
||||
key.account.notificationRelays.flow.value.flatMap {
|
||||
filterSummaryNotificationsToPubkey(
|
||||
relay = it,
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class AccountNotificationsEoseFromRandomRelaysManager(
|
||||
override fun updateFilter(
|
||||
key: AccountQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
): List<RelayBasedFilter> {
|
||||
// only loads this after the feed is built
|
||||
val defaultSince = key.feedContentStates.notifications.lastNoteCreatedAtIfFilled() ?: TimeUtils.oneWeekAgo()
|
||||
return (key.account.followsPerRelay.value.keys - key.account.notificationRelays.flow.value).flatMap {
|
||||
|
||||
+3
-3
@@ -90,7 +90,7 @@ fun filterSummaryNotificationsToPubkey(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
@@ -111,7 +111,7 @@ fun filterNotificationsToPubkey(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
@@ -142,7 +142,7 @@ fun filterJustTheLatestNotificationsToPubkeyFromRandomRelays(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ fun filterGiftWrapsToPubkey(
|
||||
pubkey: HexKey?,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (pubkey == null || pubkey.isEmpty()) return emptyList()
|
||||
if (pubkey.isNullOrEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class ChannelLoaderSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ChannelFinderQueryState>,
|
||||
) : 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
|
||||
}
|
||||
|
||||
+4
-4
@@ -40,7 +40,7 @@ fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl>
|
||||
|
||||
LocalCache.getAnyChannel(note)?.relays()?.let { set.addAll(it) }
|
||||
|
||||
note.replyTo?.map { parentNote ->
|
||||
note.replyTo?.forEach { parentNote ->
|
||||
set.addAll(parentNote.relays)
|
||||
|
||||
LocalCache.getAnyChannel(parentNote)?.relays()?.let { set.addAll(it) }
|
||||
@@ -48,7 +48,7 @@ fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl>
|
||||
parentNote.author?.inboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.replies.map { childNote ->
|
||||
note.replies.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
|
||||
LocalCache.getAnyChannel(childNote)?.relays()?.let { set.addAll(it) }
|
||||
@@ -63,7 +63,7 @@ fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl>
|
||||
}
|
||||
}
|
||||
|
||||
note.boosts.map { childNote ->
|
||||
note.boosts.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
@@ -71,7 +71,7 @@ fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl>
|
||||
return set
|
||||
}
|
||||
|
||||
fun filterMissingAddressables(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
||||
fun filterMissingAddressables(keys: List<EventFinderQueryState>): List<RelayBasedFilter> {
|
||||
val addressesPerRelay =
|
||||
mapOfSet {
|
||||
keys.forEach { key ->
|
||||
|
||||
+4
-4
@@ -36,7 +36,7 @@ fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
||||
|
||||
LocalCache.getAnyChannel(note)?.relays()?.let { set.addAll(it) }
|
||||
|
||||
note.replyTo?.map { parentNote ->
|
||||
note.replyTo?.forEach { parentNote ->
|
||||
set.addAll(parentNote.relays)
|
||||
|
||||
LocalCache.getAnyChannel(parentNote)?.relays()?.let { set.addAll(it) }
|
||||
@@ -44,7 +44,7 @@ fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
||||
parentNote.author?.inboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.replies.map { childNote ->
|
||||
note.replies.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
|
||||
LocalCache.getAnyChannel(childNote)?.relays()?.let { set.addAll(it) }
|
||||
@@ -59,7 +59,7 @@ fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
||||
}
|
||||
}
|
||||
|
||||
note.boosts.map { childNote ->
|
||||
note.boosts.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
@@ -67,7 +67,7 @@ fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
||||
return set
|
||||
}
|
||||
|
||||
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
||||
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter> {
|
||||
val eventsPerRelay =
|
||||
mapOfSet {
|
||||
keys.forEach { key ->
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class EventWatcherSubAssembler(
|
||||
allKeys: () -> Set<EventFinderQueryState>,
|
||||
) : SingleSubEoseManager<EventFinderQueryState>(client, allKeys) {
|
||||
var lastNotesOnFilter = emptyList<Note>()
|
||||
var latestEOSEs: EOSEAccountFast<Note> = EOSEAccountFast<Note>(1000)
|
||||
var latestEOSEs: EOSEAccountFast<Note> = EOSEAccountFast(1000)
|
||||
|
||||
override fun newEose(
|
||||
relay: NormalizedRelayUrl,
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class UserOutboxFinderSubAssembler(
|
||||
* 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).
|
||||
*/
|
||||
var hasTried: EOSEAccountFast<User> = EOSEAccountFast<User>(200)
|
||||
var hasTried: EOSEAccountFast<User> = EOSEAccountFast(200)
|
||||
|
||||
fun newEose(
|
||||
relay: NormalizedRelayUrl,
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class UserCardsSubAssembler(
|
||||
val trustedAccounts: Map<NormalizedRelayUrl, Set<HexKey>> =
|
||||
mapOfSet {
|
||||
accounts.forEach { account ->
|
||||
account.homeRelays.flow.value.map {
|
||||
account.homeRelays.flow.value.forEach {
|
||||
add(it, account.userProfile().pubkeyHex)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
class KindGroup(
|
||||
var count: AtomicInteger = AtomicInteger(0),
|
||||
var memory: AtomicInteger = AtomicInteger(0),
|
||||
val subs: LargeCache<String, AtomicInteger> = LargeCache<String, AtomicInteger>(),
|
||||
val relays: LargeCache<NormalizedRelayUrl, AtomicInteger> = LargeCache<NormalizedRelayUrl, AtomicInteger>(),
|
||||
val subs: LargeCache<String, AtomicInteger> = LargeCache(),
|
||||
val relays: LargeCache<NormalizedRelayUrl, AtomicInteger> = LargeCache(),
|
||||
) {
|
||||
companion object {
|
||||
const val MB: Int = 1024
|
||||
|
||||
@@ -32,7 +32,7 @@ typealias MutableTime = com.vitorpamplona.amethyst.commons.relays.MutableTime
|
||||
open class EOSEByKey<U : Any>(
|
||||
cacheSize: Int = 200,
|
||||
) {
|
||||
var followList: LruCache<U, EOSERelayList> = LruCache<U, EOSERelayList>(cacheSize)
|
||||
var followList: LruCache<U, EOSERelayList> = LruCache(cacheSize)
|
||||
|
||||
fun addOrUpdate(
|
||||
listCode: U,
|
||||
@@ -99,7 +99,7 @@ open class EOSEAccountKey<U : Any>(
|
||||
class EOSEAccountFast<T : Any>(
|
||||
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()
|
||||
|
||||
fun addOrUpdate(
|
||||
|
||||
+7
-10
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.service.tts
|
||||
|
||||
import android.content.Context
|
||||
import android.media.AudioAttributes
|
||||
import android.os.Build
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.UtteranceProgressListener
|
||||
import java.util.Locale
|
||||
@@ -79,15 +78,13 @@ class TextToSpeechEngine private constructor() {
|
||||
engine.setPitch(defaultPitch)
|
||||
engine.setSpeechRate(defaultSpeed)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val audioAttributes =
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build()
|
||||
engine.setAudioAttributes(audioAttributes)
|
||||
}
|
||||
val audioAttributes =
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build()
|
||||
engine.setAudioAttributes(audioAttributes)
|
||||
|
||||
engine.setListener(
|
||||
onStart = { onStartListener?.invoke() },
|
||||
|
||||
+2
-4
@@ -170,7 +170,7 @@ internal fun <T> Iterable<T>.fillToSize(
|
||||
transform: (List<T>) -> T,
|
||||
): List<T> {
|
||||
val capacity = ceil(size.safeDiv(count())).roundToInt()
|
||||
return map { data -> List(capacity) { data } }.flatten().chunkToSize(size, transform)
|
||||
return flatMap { data -> List(capacity) { data } }.chunkToSize(size, transform)
|
||||
}
|
||||
|
||||
internal fun <T> Iterable<T>.chunkToSize(
|
||||
@@ -207,6 +207,4 @@ internal fun Iterable<Float>.normalize(
|
||||
return values.map { scale * ((it - currentMin) / range) + min }
|
||||
}
|
||||
|
||||
private fun Int.safeDiv(value: Int): Float {
|
||||
return if (value == 0) return 0F else this / value.toFloat()
|
||||
}
|
||||
private fun Int.safeDiv(value: Int): Float = if (value == 0) 0F else this / value.toFloat()
|
||||
|
||||
@@ -108,7 +108,7 @@ fun LoadOrCreateNote(
|
||||
content: @Composable (Note?) -> Unit,
|
||||
) {
|
||||
var note by
|
||||
remember(event.id) { mutableStateOf<Note?>(accountViewModel.getNoteIfExists(event.id)) }
|
||||
remember(event.id) { mutableStateOf(accountViewModel.getNoteIfExists(event.id)) }
|
||||
|
||||
if (note == null) {
|
||||
LaunchedEffect(key1 = event.id) {
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ fun SensitivityWarning(
|
||||
@Composable
|
||||
fun ContentWarningNotePreview() {
|
||||
ThemeComparisonColumn {
|
||||
ContentWarningNote({})
|
||||
ContentWarningNote {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -49,7 +49,7 @@ class ChannelFeedContentState(
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
// 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()
|
||||
var scrolltoTopPending = false
|
||||
|
||||
@@ -64,7 +64,7 @@ class ChannelFeedContentState(
|
||||
viewModelScope.launch(Dispatchers.IO) { _scrollToTop.emit(_scrollToTop.value + 1) }
|
||||
}
|
||||
|
||||
suspend fun sentToTop() {
|
||||
fun sentToTop() {
|
||||
scrolltoTopPending = false
|
||||
}
|
||||
|
||||
@@ -98,10 +98,10 @@ class ChannelFeedContentState(
|
||||
if (notes.isEmpty()) {
|
||||
_feedContent.tryEmit(ChannelFeedState.Empty)
|
||||
} else if (currentState is ChannelFeedState.Loaded) {
|
||||
currentState.feed.tryEmit(LoadedFeedState<Channel>(notes, localFilter.showHiddenKey()))
|
||||
currentState.feed.tryEmit(LoadedFeedState(notes, localFilter.showHiddenKey()))
|
||||
} else {
|
||||
_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
|
||||
* 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.AnimationState
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.
|
||||
*/
|
||||
package com.vitorpamplona.myapplication
|
||||
package com.vitorpamplona.amethyst.ui.layouts
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
|
||||
@@ -32,8 +32,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.myapplication.DisappearingBottomBar
|
||||
import com.vitorpamplona.myapplication.DisappearingFloatingButton
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ fun DisplayAccount(
|
||||
accountSessionManager: AccountSessionManager,
|
||||
) {
|
||||
var baseUser by remember(acc) {
|
||||
mutableStateOf<User?>(
|
||||
mutableStateOf(
|
||||
decodePublicKeyAsHexOrNull(acc.npub)?.let {
|
||||
LocalCache.getUserIfExists(it)
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ class Nav(
|
||||
) {
|
||||
navigationScope.launch {
|
||||
controller.navigate(route) {
|
||||
popUpTo<T>(klass) { inclusive = true }
|
||||
popUpTo(klass) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -323,7 +323,7 @@ suspend fun routeEditDraftTo(
|
||||
is ChatroomKeyable -> {
|
||||
val room = draft.chatroomKey(account.userProfile().pubkeyHex)
|
||||
account.chatroomList.getOrCreatePrivateChatroom(room)
|
||||
return Route.Room(room, draftId = note.idHex)
|
||||
Route.Room(room, draftId = note.idHex)
|
||||
}
|
||||
|
||||
is TextNoteEvent -> {
|
||||
|
||||
@@ -98,7 +98,7 @@ fun HiddenNotePreview() {
|
||||
ThemeComparisonColumn(
|
||||
toPreview = {
|
||||
HiddenNote(
|
||||
reports = persistentSetOf<Note>(),
|
||||
reports = persistentSetOf(),
|
||||
isHiddenAuthor = true,
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = EmptyNav(),
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ fun ObserveAllStatusesToAvoidSwitchigAllTheTime(
|
||||
statuses: ImmutableList<AddressableNote>,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
statuses.map {
|
||||
statuses.forEach {
|
||||
EventFinderFilterAssemblerSubscription(it, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@ import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun NoteCompose(
|
||||
@@ -536,7 +537,7 @@ fun ClickableNote(
|
||||
|
||||
nav.nav {
|
||||
if (redirectToNote.event is DraftWrapEvent) {
|
||||
with(Dispatchers.IO) {
|
||||
withContext(Dispatchers.IO) {
|
||||
routeEditDraftTo(redirectToNote, accountViewModel.account)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -481,7 +481,7 @@ private fun ReactionDetailGallery(
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val defaultBackgroundColor = MaterialTheme.colorScheme.background
|
||||
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
|
||||
val backgroundColor = remember { mutableStateOf(defaultBackgroundColor) }
|
||||
|
||||
val hasReactions by observeNoteReferences(baseNote, accountViewModel)
|
||||
|
||||
@@ -757,7 +757,7 @@ private fun SlidingAnimationCount(
|
||||
if (accountViewModel.settings.isPerformanceMode()) {
|
||||
TextCount(baseCount, textColor)
|
||||
} else {
|
||||
AnimatedContent<Int>(
|
||||
AnimatedContent(
|
||||
targetState = baseCount,
|
||||
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
||||
label = "SlidingAnimationCount",
|
||||
|
||||
@@ -86,8 +86,8 @@ fun ReplyInformationChannel(
|
||||
onUserTagClick: (User) -> Unit,
|
||||
) {
|
||||
FlowRow {
|
||||
if (mentions != null && mentions.isNotEmpty()) {
|
||||
if (replyTo != null && replyTo.isNotEmpty()) {
|
||||
if (!mentions.isNullOrEmpty()) {
|
||||
if (!replyTo.isNullOrEmpty()) {
|
||||
Text(
|
||||
stringRes(id = R.string.replying_to),
|
||||
fontSize = 13.sp,
|
||||
|
||||
@@ -32,7 +32,7 @@ import kotlin.math.round
|
||||
private const val YEAR_DATE_FORMAT = "MMM dd, yyyy"
|
||||
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 monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ class PollNoteViewModel : ViewModel() {
|
||||
valueMinimum?.let { minimum ->
|
||||
valueMaximum?.let { 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
|
||||
class DraftTagState {
|
||||
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)
|
||||
val versions = _versions.debounce(1000)
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ class UserSuggestionState(
|
||||
val account: Account,
|
||||
val requireAtSymbol: Boolean = true,
|
||||
) {
|
||||
val invalidations = MutableStateFlow<Int>(0)
|
||||
val invalidations = MutableStateFlow(0)
|
||||
val currentWord = MutableStateFlow("")
|
||||
val searchDataSourceState = SearchQueryState(MutableStateFlow(""), account)
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ fun DisplayZapSplits(
|
||||
remember(noteEvent) {
|
||||
val list = noteEvent.zapSplitSetup()
|
||||
if (list.isEmpty() && useAuthorIfEmpty) {
|
||||
listOf<ZapSplitSetup>(
|
||||
listOf(
|
||||
ZapSplitSetup(
|
||||
pubKeyHex = noteEvent.pubKey,
|
||||
relay = null,
|
||||
|
||||
@@ -116,13 +116,13 @@ private fun RenderPledgeAmount(
|
||||
) {
|
||||
val repliesState by observeNoteReplies(baseNote, accountViewModel)
|
||||
var reward by remember {
|
||||
mutableStateOf<String>(
|
||||
mutableStateOf(
|
||||
showAmount(baseReward.amount),
|
||||
)
|
||||
}
|
||||
|
||||
var hasPledge by remember {
|
||||
mutableStateOf<Boolean>(
|
||||
mutableStateOf(
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
+4
-4
@@ -58,7 +58,7 @@ fun DisplayUncitedHashtags(
|
||||
nav: INav,
|
||||
) {
|
||||
val unusedHashtags by
|
||||
produceState(initialValue = emptyList<String>()) {
|
||||
produceState(initialValue = emptyList()) {
|
||||
val tagsInEvent = event.hashtags()
|
||||
if (tagsInEvent.isNotEmpty()) {
|
||||
val state = CachedRichTextParser.parseText(content, event.tags.toImmutableListOfLists(), callbackUri)
|
||||
@@ -66,15 +66,15 @@ fun DisplayUncitedHashtags(
|
||||
val tagsInContent =
|
||||
state
|
||||
.paragraphs
|
||||
.map {
|
||||
it.words.mapNotNull {
|
||||
.flatMap { paragraphState ->
|
||||
paragraphState.words.mapNotNull {
|
||||
if (it is HashTagSegment) {
|
||||
it.hashtag
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
val unusedHashtags =
|
||||
tagsInEvent.filterNot { eventTag ->
|
||||
|
||||
+2
-2
@@ -474,7 +474,7 @@ open class CommentPostViewModel :
|
||||
myMultiOrchestrator.upload(
|
||||
alt,
|
||||
contentWarningReason,
|
||||
MediaCompressor.Companion.intToCompressorQuality(mediaQuality),
|
||||
MediaCompressor.intToCompressorQuality(mediaQuality),
|
||||
server,
|
||||
account,
|
||||
context,
|
||||
@@ -703,5 +703,5 @@ open class CommentPostViewModel :
|
||||
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
|
||||
|
||||
@Composable
|
||||
public fun RenderEmojiPack(
|
||||
fun RenderEmojiPack(
|
||||
baseNote: Note,
|
||||
actionable: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
@@ -86,7 +86,7 @@ public fun RenderEmojiPack(
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
public fun RenderEmojiPack(
|
||||
fun RenderEmojiPack(
|
||||
noteEvent: EmojiPackEvent,
|
||||
baseNote: Note,
|
||||
actionable: Boolean,
|
||||
|
||||
@@ -649,12 +649,12 @@ fun RenderPollGameResultsPreview() {
|
||||
Column(Modifier.padding(10.dp)) {
|
||||
RenderPoll(
|
||||
note,
|
||||
false,
|
||||
true,
|
||||
2,
|
||||
remember { mutableStateOf(Color.Transparent) },
|
||||
mockAccountViewModel(),
|
||||
EmptyNav(),
|
||||
makeItShort = false,
|
||||
canPreview = true,
|
||||
quotesLeft = 2,
|
||||
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = EmptyNav(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -758,12 +758,12 @@ fun RenderPollColorResultsPreview() {
|
||||
Column(Modifier.padding(10.dp)) {
|
||||
RenderPoll(
|
||||
note,
|
||||
false,
|
||||
true,
|
||||
2,
|
||||
remember { mutableStateOf(Color.Transparent) },
|
||||
mockAccountViewModel(),
|
||||
EmptyNav(),
|
||||
makeItShort = false,
|
||||
canPreview = true,
|
||||
quotesLeft = 2,
|
||||
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = EmptyNav(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
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.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
@@ -102,7 +101,7 @@ fun DisplayUncitedUsers(
|
||||
nav: INav,
|
||||
) {
|
||||
@Suppress("ProduceStateDoesNotAssignValue")
|
||||
val uncitedUsers by produceState(initialValue = emptyList<User>()) {
|
||||
val uncitedUsers by produceState(initialValue = emptyList()) {
|
||||
val users = event.groupKeySetWithoutOwner() - event.citedUsers()
|
||||
if (users.isNotEmpty()) {
|
||||
val newUsers = accountViewModel.loadUsersSync(users.toList())
|
||||
|
||||
@@ -139,12 +139,12 @@ fun TorrentCommentPreview() {
|
||||
toPreview = {
|
||||
RenderTorrentComment(
|
||||
comment,
|
||||
false,
|
||||
true,
|
||||
3,
|
||||
ReplyRenderType.FULL,
|
||||
remember { mutableStateOf(Color.Transparent) },
|
||||
EmptyState,
|
||||
makeItShort = false,
|
||||
canPreview = true,
|
||||
quotesLeft = 3,
|
||||
unPackReply = ReplyRenderType.FULL,
|
||||
backgroundColor = remember { mutableStateOf(Color.Transparent) },
|
||||
editState = EmptyState,
|
||||
accountViewModel = accountViewModel,
|
||||
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(
|
||||
key: String,
|
||||
|
||||
@@ -65,7 +65,9 @@ class CompositionObserver(
|
||||
private val vmStore: StoreOwnerRegistry,
|
||||
private val key: Any,
|
||||
) : RememberObserver {
|
||||
override fun onRemembered() {}
|
||||
override fun onRemembered() {
|
||||
// No action needed when remembered — registration happens at construction time.
|
||||
}
|
||||
|
||||
override fun onForgotten() = vmStore.composableDetached(key)
|
||||
|
||||
|
||||
+5
-5
@@ -357,10 +357,10 @@ class AccountViewModel(
|
||||
|
||||
return if (isPostHidden || isDecryptedPostHidden) {
|
||||
// Spam + Blocked Users + Hidden Words + Sensitive Content
|
||||
NoteComposeReportState(isPostHidden, false, false, isHiddenAuthor)
|
||||
NoteComposeReportState(isPostHidden, isAcceptable = false, canPreview = false, isHiddenAuthor = isHiddenAuthor)
|
||||
} else if (isFromLoggedIn || isFromLoggedInFollow) {
|
||||
// No need to process if from trusted people
|
||||
NoteComposeReportState(isPostHidden, true, true, isHiddenAuthor)
|
||||
NoteComposeReportState(isPostHidden, isAcceptable = true, canPreview = true, isHiddenAuthor = isHiddenAuthor)
|
||||
} else {
|
||||
val newCanPreview = !note.hasAnyReports()
|
||||
|
||||
@@ -368,7 +368,7 @@ class AccountViewModel(
|
||||
|
||||
if (newCanPreview && newIsAcceptable) {
|
||||
// No need to process reports if nothing is wrong
|
||||
NoteComposeReportState(isPostHidden, true, true, false)
|
||||
NoteComposeReportState(isPostHidden, isAcceptable = true, canPreview = true, isHiddenAuthor = false)
|
||||
} else {
|
||||
NoteComposeReportState(
|
||||
isPostHidden,
|
||||
@@ -511,7 +511,7 @@ class AccountViewModel(
|
||||
}.toMutableMap()
|
||||
|
||||
val results =
|
||||
mapNotNullAsync<CombinedZap, DecryptedInfo>(
|
||||
mapNotNullAsync(
|
||||
zaps.filter { (it.request.event as? LnZapRequestEvent)?.isPrivateZap() == true },
|
||||
) { next ->
|
||||
val info = innerDecryptAmountMessage(next.request, next.response)
|
||||
@@ -1609,7 +1609,7 @@ class AccountViewModel(
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5000),
|
||||
emptySet<HexKey>(),
|
||||
emptySet(),
|
||||
)
|
||||
|
||||
val draftNoteCache = CachedDraftNotes(this)
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ fun ChatBubbleLayout(
|
||||
private fun BubblePreview() {
|
||||
val bgColor =
|
||||
remember {
|
||||
mutableStateOf<Color>(Color.Transparent)
|
||||
mutableStateOf(Color.Transparent)
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
+2
-2
@@ -222,8 +222,8 @@ fun RenderRelayLinePreview() {
|
||||
"wss://nos.lol",
|
||||
"http://icon.com/icon.ico",
|
||||
Modifier,
|
||||
true,
|
||||
true,
|
||||
showPicture = true,
|
||||
loadRobohash = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ fun filterNip04DMs(
|
||||
account: Account?,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
if (group == null || group.isEmpty() || account == null) return null
|
||||
if (group.isNullOrEmpty() || account == null) return null
|
||||
|
||||
val userOutboxRelays = account.homeRelays.flow.value
|
||||
val userInboxRelays = account.dmRelays.flow.value
|
||||
|
||||
+2
-2
@@ -73,14 +73,14 @@ fun NewChatroomSubjectDialog(
|
||||
Surface {
|
||||
val groupName =
|
||||
remember {
|
||||
mutableStateOf<String>(
|
||||
mutableStateOf(
|
||||
accountViewModel.account.chatroomList.rooms
|
||||
.get(room)
|
||||
?.subject
|
||||
?.value ?: "",
|
||||
)
|
||||
}
|
||||
val message = remember { mutableStateOf<String>("") }
|
||||
val message = remember { mutableStateOf("") }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
|
||||
+1
-1
@@ -596,7 +596,7 @@ class ChatNewMessageViewModel :
|
||||
toUsersTagger.run()
|
||||
|
||||
val users = toUsersTagger.pTags?.mapTo(mutableSetOf()) { it.pubkeyHex }
|
||||
if (users == null || users.isEmpty()) {
|
||||
if (users.isNullOrEmpty()) {
|
||||
room = null
|
||||
updateNIP17StatusFromRoom()
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
fun filterMessagesToPublicChat(
|
||||
channel: PublicChatChannel,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? =
|
||||
): List<RelayBasedFilter> =
|
||||
channel.relays().toSet().map {
|
||||
RelayBasedFilter(
|
||||
relay = it,
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ fun filterMyMessagesToLiveActivities(
|
||||
channel: LiveActivitiesChannel,
|
||||
pubKey: HexKey,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? =
|
||||
): List<RelayBasedFilter> =
|
||||
channel.relays().toSet().map {
|
||||
RelayBasedFilter(
|
||||
relay = it,
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ class ChannelMetadataViewModel : ViewModel() {
|
||||
|
||||
val template =
|
||||
if (event != null) {
|
||||
val hint = EventHintBundle<ChannelCreateEvent>(event, channel.relays().firstOrNull())
|
||||
val hint = EventHintBundle(event, channel.relays().firstOrNull())
|
||||
|
||||
ChannelMetadataEvent.build(
|
||||
channelName.value.text,
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class FollowingEphemeralChatSubAssembler(
|
||||
override fun updateFilter(
|
||||
key: ChatroomListState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? =
|
||||
): List<RelayBasedFilter> =
|
||||
listOfNotNull(
|
||||
filterFollowingEphemeralChats(key.account.ephemeralChatList.liveEphemeralChatList.value, since),
|
||||
).flatten()
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class FollowingPublicChatSubAssembler(
|
||||
override fun updateFilter(
|
||||
key: ChatroomListState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? =
|
||||
): List<RelayBasedFilter> =
|
||||
listOfNotNull(
|
||||
filterLastMessageFollowingPublicChats(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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
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.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
||||
+1
-2
@@ -117,8 +117,7 @@ open class DiscoverLiveFeedFilter(
|
||||
|
||||
fun convertStatusToOrder(event: LiveActivitiesEvent?): Int {
|
||||
if (event == null) return 0
|
||||
val url = event.streaming()
|
||||
if (url == null) return 0
|
||||
val url = event.streaming() ?: return 0
|
||||
return when (event.status()) {
|
||||
StatusTag.STATUS.LIVE -> {
|
||||
if (OnlineChecker.isCachedAndOffline(url)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user