Merge branch 'main' into claude/event-sync-screen-sYGtN

This commit is contained in:
Vitor Pamplona
2026-03-17 14:17:13 -04:00
committed by GitHub
7 changed files with 88 additions and 4 deletions
@@ -325,7 +325,7 @@ object LocalCache : ILocalCache, ICacheProvider {
newFilter.init()
observables.put(newFilter, newFilter)
observables[newFilter] = newFilter
awaitClose {
observables.remove(newFilter)
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* 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.amethyst.ui.screen.loggedIn.profile.datasource
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
fun filterUserProfileLastSeen(
users: Map<NormalizedRelayUrl, Set<String>>,
since: SincePerRelayMap?,
): List<RelayBasedFilter> {
if (users.isEmpty()) return emptyList()
return users.map {
RelayBasedFilter(
relay = it.key,
filter =
Filter(
authors = it.value.sorted(),
since = since?.get(it.key)?.time,
),
)
}
}
@@ -52,6 +52,7 @@ class UserProfileMetadataFilterSubAssembler(
return listOfNotNull(
filterUserProfileMetadata(userPerRelay, since),
filterUserProfileLists(userPerRelay, since),
filterUserProfileLastSeen(userPerRelay, since),
).flatten()
}
@@ -45,6 +45,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
@@ -65,6 +66,7 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.note.DrawPlayName
import com.vitorpamplona.amethyst.ui.note.ObserveAndRenderNIP05VerifiedSymbol
import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.note.toShortDisplay
import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -78,11 +80,16 @@ import com.vitorpamplona.amethyst.ui.theme.SpacedBy3dp
import com.vitorpamplona.amethyst.ui.theme.SpacedBy5dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
import com.vitorpamplona.quartz.nip39ExtIdentities.IdentityClaimTag
import com.vitorpamplona.quartz.nip39ExtIdentities.MastodonIdentity
import com.vitorpamplona.quartz.nip39ExtIdentities.TelegramIdentity
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
private const val IDENTITY_ICON_CACHE_KEY = 0
@@ -200,6 +207,8 @@ fun DrawAdditionalInfo(
}
}
DisplayLastSeen(baseUser, accountViewModel)
DisplayNip05ProfileStatus(baseUser, accountViewModel)
val lud16 =
@@ -281,6 +290,33 @@ fun DrawAdditionalInfo(
}
}
@Composable
fun DisplayLastSeen(
user: User,
accountViewModel: AccountViewModel,
) {
val lastSeenFlow =
remember {
accountViewModel.account.cache
.observeLatestEvent<Event>(Filter(authors = listOf(user.pubkeyHex)))
.map {
it?.createdAt
}.flowOn(Dispatchers.IO)
}
val lastSeen by lastSeenFlow.collectAsStateWithLifecycle(null)
lastSeen?.let { timestamp ->
val context = LocalContext.current
Text(
text = stringRes(R.string.last_seen, timeAgo(timestamp, context)),
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
@Composable
fun DisplayNip05ProfileStatus(
user: User,
@@ -71,6 +71,7 @@ fun DrawBanner(
contentDescription = stringRes(id = R.string.profile_image),
contentScale = ContentScale.Crop,
placeholder = painterRes(R.drawable.profile_banner, 1),
error = painterRes(R.drawable.profile_banner, 1),
modifier =
Modifier
.fillMaxWidth()
@@ -82,6 +82,7 @@ enum class TransactionFilter {
}
private const val NWC_TIMEOUT_MS = 30_000L
private const val PAYMENT_FAILED = "Payment failed"
class WalletViewModel : ViewModel() {
private var account: Account? = null
@@ -301,14 +302,14 @@ class WalletViewModel : ViewModel() {
is PayInvoiceErrorResponse -> {
_sendState.value =
SendState.Error(
response.error?.message ?: "Payment failed",
response.error?.message ?: PAYMENT_FAILED,
)
}
is NwcErrorResponse -> {
_sendState.value =
SendState.Error(
response.error?.message ?: "Payment failed",
response.error?.message ?: PAYMENT_FAILED,
)
}
@@ -318,7 +319,7 @@ class WalletViewModel : ViewModel() {
}
}
} catch (e: Exception) {
_sendState.value = SendState.Error(e.message ?: "Payment failed")
_sendState.value = SendState.Error(e.message ?: PAYMENT_FAILED)
}
}
}
+1
View File
@@ -1850,4 +1850,5 @@
<string name="dms">DMs</string>
<string name="profiles">profiles</string>
<string name="relay_settings_lower">relay settings</string>
<string name="last_seen">Last seen %1$s ago</string>
</resources>