From 0a5aed7ec5f819f9cbe1c07345f1dd85ca7a6954 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 22:59:42 +0000 Subject: [PATCH] refactor: share the user avatar image, score tags and metadata observers The avatar is the second most common dependency of the app's composables (after AccountViewModel). This moves its rendering and the observers under it into commonsUI, leaving the app with the settings/account reads. - Observers: the app's observeUserInfo, observeUserAboutMe, observeUserBanner and observeUserPicture were copies of the commonsUI relayClient/user ones, and its UserFinderFilterAssemblerSubscription (user, Account, dataSource) a copy of the shared subscription. They now delegate, keeping their signatures so no call site changes. The shared observeUserPicture gains the app's `subscribe` flag. observeUserName stays app-side: it applies the account's contact-card petname. (The shared subscription remembers per user *and* account, so an account switch no longer reuses the previous account's query state.) - ScoreTag / ScoreTagRegular / Small / Smallest move to ui/note. - RobohashAsyncImage, RobohashFallbackAsyncImage and GifProfilePicture move to commonsUI jvmAndroid ui/components (they tag requests with the jvmAndroid-only LocalBlossomCacheRedirectInterceptor header). The one Android API, starting/stopping an animated avatar's Animatable drawable, becomes AnimatedImageAutoPlay: an expect in jvmAndroid with the Android actual and a desktop no-op. The Coil request uses LocalPlatformContext instead of LocalContext. - isAnimatedMediaUrl, a pure URL check, moves to commons richtext. - UserPictureImage(userHex, picture, name, size, modifier, loadProfilePicture, loadRobohash, autoPlayGif) in commonsUI jvmAndroid ui/note is the display half of InnerUserPicture, which keeps reading the three settings. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0168wY9t7i9NC5u3svyMxEz6 --- .../consent/ConnectedAccountRow.kt | 2 +- .../consent/SignerConsentActivity.kt | 2 +- .../napplet/NappletConsentActivity.kt | 2 +- .../compose/RelayAuthPromptHost.kt | 2 +- .../UserFinderFilterAssemblerSubscription.kt | 12 +- .../reqCommand/user/UserObservers.kt | 96 ++------------ .../ui/components/ConcordInviteCard.kt | 1 + .../amethyst/ui/components/MyAsyncImage.kt | 9 +- .../amethyst/ui/components/RelayGroupCard.kt | 1 + .../bottombars/GroupBottomBarEntries.kt | 2 +- .../drawer/AccountSwitchBottomSheet.kt | 2 +- .../ui/navigation/drawer/DrawerContent.kt | 2 +- .../topbars/UserDrawerSearchTopBar.kt | 2 +- .../amethyst/ui/note/MultiSetCompose.kt | 3 +- .../amethyst/ui/note/NoteCompose.kt | 2 +- .../amethyst/ui/note/RelayListRow.kt | 2 +- .../amethyst/ui/note/UserProfilePicture.kt | 118 ++--------------- .../amethyst/ui/note/types/Badge.kt | 2 +- .../amethyst/ui/note/types/CommunityHeader.kt | 2 +- .../amethyst/ui/note/types/PodcastPeople.kt | 2 +- .../ui/note/types/RelayGroupMessage.kt | 2 +- .../ProfileAppRecommendationsScreen.kt | 2 +- .../backups/BackupConflictEventViews.kt | 2 +- .../backups/BackupConflictListViews.kt | 2 +- .../badges/profile/ProfileBadgesScreen.kt | 2 +- .../chats/geohashChat/GeohashChatScreen.kt | 2 +- .../marmotGroup/MarmotGroupIconEditor.kt | 2 +- .../concord/ConcordHomeScreen.kt | 2 +- .../concord/ConcordMetadataForm.kt | 2 +- .../header/ShortEphemeralChatChannelHeader.kt | 2 +- .../header/LongPublicChatChannelHeader.kt | 2 +- .../header/ShortPublicChatChannelHeader.kt | 2 +- .../relayGroup/RelayGroupChannelListScreen.kt | 2 +- .../relayGroup/RelayGroupDiscoveryScreen.kt | 2 +- .../relayGroup/RelayGroupParentPicker.kt | 2 +- .../send/ChannelFileUploadDialog.kt | 2 +- .../chats/rooms/ChatroomHeaderCompose.kt | 2 +- .../profile/header/badges/DisplayBadges.kt | 4 +- .../ui/screen/loggedIn/qrcode/ShowQRScreen.kt | 2 +- .../relayauth/RelayAuthSettingsScreen.kt | 2 +- .../ActiveSubscriptionsScreen.kt | 2 +- .../commons/richtext/AnimatedMediaUrl.kt | 30 +++++ .../AnimatedImageAutoPlay.android.kt | 43 +++++++ .../relayClient/user/UserMetadataObservers.kt | 7 +- .../amethyst/commons/ui/note/ScoreTag.kt | 119 ++++++++++++++++++ .../ui/components/RobohashAsyncImage.kt | 43 +++---- .../commons/ui/note/UserPictureImage.kt | 73 +++++++++++ .../components/AnimatedImageAutoPlay.jvm.kt | 32 +++++ 48 files changed, 379 insertions(+), 278 deletions(-) create mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/AnimatedMediaUrl.kt create mode 100644 commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.android.kt create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ScoreTag.kt rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons}/ui/components/RobohashAsyncImage.kt (88%) create mode 100644 commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/note/UserPictureImage.kt create mode 100644 commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.jvm.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/ConnectedAccountRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/ConnectedAccountRow.kt index c4e20fe1cb..58950306c6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/ConnectedAccountRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/ConnectedAccountRow.kt @@ -32,7 +32,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage /** * The account a signer request acts as — avatar + display name — so it's clear WHICH logged-in diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/SignerConsentActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/SignerConsentActivity.kt index 2f627041d9..7d610c2ffd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/SignerConsentActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/connectedApps/consent/SignerConsentActivity.kt @@ -95,11 +95,11 @@ import com.vitorpamplona.amethyst.commons.resources.nip46_signer_batch_select_no import com.vitorpamplona.amethyst.commons.resources.nip46_signer_batch_signing_as import com.vitorpamplona.amethyst.commons.resources.nip46_signer_batch_title import com.vitorpamplona.amethyst.commons.resources.nip46_signer_messages_with +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.service.call.CallSessionBridge -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.theme.AmethystTheme import com.vitorpamplona.quartz.nip01Core.core.Event diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/napplet/NappletConsentActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/napplet/NappletConsentActivity.kt index 96ddf7fb70..49e9bb54c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/napplet/NappletConsentActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/napplet/NappletConsentActivity.kt @@ -70,8 +70,8 @@ import com.vitorpamplona.amethyst.commons.resources.napplet_consent_deny_always import com.vitorpamplona.amethyst.commons.resources.napplet_consent_hide_event import com.vitorpamplona.amethyst.commons.resources.napplet_consent_not_now import com.vitorpamplona.amethyst.commons.resources.napplet_consent_show_event +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.theme.AmethystTheme /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/authCommand/compose/RelayAuthPromptHost.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/authCommand/compose/RelayAuthPromptHost.kt index 81d8ae1207..4a09f6706a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/authCommand/compose/RelayAuthPromptHost.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/authCommand/compose/RelayAuthPromptHost.kt @@ -97,13 +97,13 @@ import com.vitorpamplona.amethyst.commons.resources.relay_auth_why_read_venue import com.vitorpamplona.amethyst.commons.resources.relay_auth_why_send_dm import com.vitorpamplona.amethyst.commons.resources.relay_auth_why_thread import com.vitorpamplona.amethyst.commons.resources.relay_auth_why_thread_with +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.RelayIconFilter import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.Address diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt index 9d67b5d442..2774e6f8c0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt @@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderQueryState import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider +import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderFilterAssemblerSubscription as SharedUserFinderSubscription @SuppressLint("StateFlowValueCalledInComposition") @Composable @@ -48,16 +49,7 @@ fun UserFinderFilterAssemblerSubscription( user: User, forAccount: Account, dataSource: UserFinderFilterAssembler, -) { - // different screens get different states - // even if they are tracking the same tag. - val state = - remember(user) { - UserFinderQueryState(user, forAccount) - } - - LifecycleAwareKeyDataSourceSubscription(state, dataSource) -} +) = SharedUserFinderSubscription(user, forAccount, dataSource) /** * Watches all the users that are cited in the parent post. diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserObservers.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserObservers.kt index b314ab7cd0..691179b7e7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserObservers.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserObservers.kt @@ -47,10 +47,13 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.sample +import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserAboutMe as sharedObserveUserAboutMe +import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserBanner as sharedObserveUserBanner +import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo as sharedObserveUserInfo +import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserPicture as sharedObserveUserPicture @OptIn(ExperimentalCoroutinesApi::class) @Composable @@ -86,113 +89,30 @@ fun observeUserNickname( return flow.collectAsStateWithLifecycle(remember(user) { contactCards.cachedNickname(user) }) } -@OptIn(ExperimentalCoroutinesApi::class) @Composable fun observeUserAboutMe( user: User, accountViewModel: AccountViewModel, -): State { - // Subscribe in the relay for changes in the metadata of this user. - UserFinderFilterAssemblerSubscription(user, accountViewModel) +): State = sharedObserveUserAboutMe(user, accountViewModel.dataSources().userFinder, accountViewModel.account) - // Subscribe in the LocalCache for changes that arrive in the device - val flow = - remember(user) { - user.metadata().flow.map { - it?.info?.about ?: "" - } - } - - return flow.collectAsStateWithLifecycle( - user - .metadataOrNull() - ?.flow - ?.value - ?.info - ?.about ?: "", - ) -} - -@OptIn(ExperimentalCoroutinesApi::class) @Composable fun observeUserInfo( user: User, accountViewModel: AccountViewModel, -): State { - // Subscribe in the relay for changes in the metadata of this user. - UserFinderFilterAssemblerSubscription(user, accountViewModel) +): State = sharedObserveUserInfo(user, accountViewModel.dataSources().userFinder, accountViewModel.account) - // Subscribe in the LocalCache for changes that arrive in the device - return user.metadata().flow.collectAsStateWithLifecycle() -} - -@SuppressLint("StateFlowValueCalledInComposition") -@OptIn(ExperimentalCoroutinesApi::class) @Composable fun observeUserBanner( user: User, accountViewModel: AccountViewModel, -): State { - // Subscribe in the relay for changes in the metadata of this user. - UserFinderFilterAssemblerSubscription(user, accountViewModel) +): State = sharedObserveUserBanner(user, accountViewModel.dataSources().userFinder, accountViewModel.account) - // Subscribe in the LocalCache for changes that arrive in the device - val flow = - remember(user) { - user - .metadata() - .flow - .map { - it?.info?.banner - }.distinctUntilChanged() - } - - return flow.collectAsStateWithLifecycle( - user - .metadataOrNull() - ?.flow - ?.value - ?.info - ?.banner, - ) -} - -@SuppressLint("StateFlowValueCalledInComposition") -@OptIn(ExperimentalCoroutinesApi::class) @Composable fun observeUserPicture( user: User, accountViewModel: AccountViewModel, subscribe: Boolean = true, -): State { - // Subscribe in the relay for changes in the metadata of this user. - // Callers that already hold a single shared subscription for the same user - // (e.g. an author avatar that also observes the contact-card score) can pass - // subscribe = false to avoid setting up a redundant relay subscription. - if (subscribe) { - UserFinderFilterAssemblerSubscription(user, accountViewModel) - } - - // Subscribe in the LocalCache for changes that arrive in the device - val flow = - remember(user) { - user - .metadata() - .flow - .map { - it?.info?.picture - }.distinctUntilChanged() - } - - return flow.collectAsStateWithLifecycle( - user - .metadataOrNull() - ?.flow - ?.value - ?.info - ?.picture, - ) -} +): State = sharedObserveUserPicture(user, accountViewModel.dataSources().userFinder, accountViewModel.account, subscribe) @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ConcordInviteCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ConcordInviteCard.kt index 8276f2016b..ab6f170785 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ConcordInviteCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ConcordInviteCard.kt @@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.concord_home_title import com.vitorpamplona.amethyst.commons.resources.concord_invite_card_join import com.vitorpamplona.amethyst.commons.resources.concord_invite_card_subtitle +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MyAsyncImage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MyAsyncImage.kt index 757b7e4244..6446525a49 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MyAsyncImage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MyAsyncImage.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.layout.ContentScale import coil3.compose.AsyncImagePainter import coil3.compose.SubcomposeAsyncImage import coil3.compose.SubcomposeAsyncImageContent +import com.vitorpamplona.amethyst.commons.richtext.isAnimatedMediaUrl import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation import com.vitorpamplona.amethyst.commons.ui.note.DownloadForOfflineIcon import com.vitorpamplona.amethyst.commons.ui.theme.Size40dp @@ -154,11 +155,3 @@ fun MyAsyncImage( } } } - -fun isAnimatedMediaUrl(url: String): Boolean = - url.endsWith(".gif", ignoreCase = true) || - url.contains(".gif?", ignoreCase = true) || - url.contains(".gif#", ignoreCase = true) || - url.endsWith(".avif", ignoreCase = true) || - url.contains(".avif?", ignoreCase = true) || - url.contains(".avif#", ignoreCase = true) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RelayGroupCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RelayGroupCard.kt index ef9bf4b422..7d7ac96c3a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RelayGroupCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RelayGroupCard.kt @@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_group_badge_invite_onl import com.vitorpamplona.amethyst.commons.resources.relay_group_badge_private import com.vitorpamplona.amethyst.commons.resources.relay_group_member_count import com.vitorpamplona.amethyst.commons.resources.relay_group_open +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt index 9add6f2fa2..428b2ac946 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/bottombars/GroupBottomBarEntries.kt @@ -41,10 +41,10 @@ import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChann import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.concord_home_title +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.ChannelFinderFilterAssemblerSubscription -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.rememberConcordImageModel import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt index 02f8689296..c828e83cee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/AccountSwitchBottomSheet.kt @@ -71,6 +71,7 @@ import com.vitorpamplona.amethyst.commons.resources.scheduled_posts_logout_toast import com.vitorpamplona.amethyst.commons.resources.scheduled_posts_logout_toast_zero import com.vitorpamplona.amethyst.commons.resources.scheduled_posts_logout_warning import com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPostStatus +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.loadPluralStringRes import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -79,7 +80,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.toShortDisplay import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/DrawerContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/DrawerContent.kt index efd48a5e86..77cf8b53a3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/DrawerContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/drawer/DrawerContent.kt @@ -116,6 +116,7 @@ import com.vitorpamplona.amethyst.commons.resources.share_hls_video import com.vitorpamplona.amethyst.commons.resources.show_npub_as_a_qr_code import com.vitorpamplona.amethyst.commons.resources.status_update import com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPostStatus +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -146,7 +147,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUse import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserStatuses import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.layouts.PermanentDrawerWidth import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarCatalog import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarItemDef diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/UserDrawerSearchTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/UserDrawerSearchTopBar.kt index cc1d87909b..4998b3ae15 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/UserDrawerSearchTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/topbars/UserDrawerSearchTopBar.kt @@ -36,6 +36,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.your_profile_image import com.vitorpamplona.amethyst.commons.search.SearchQuery +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.commons.ui.note.ArrowBackIcon @@ -45,7 +46,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.HeaderPictureModifier import com.vitorpamplona.amethyst.commons.ui.theme.Size22Modifier import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.layouts.LocalScreenLayout import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt index 775488a32a..f3d5b37d0f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt @@ -77,10 +77,12 @@ import com.vitorpamplona.amethyst.commons.resources.nutzap import com.vitorpamplona.amethyst.commons.resources.profile_image import com.vitorpamplona.amethyst.commons.richtext.CachedRichTextParser import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.FollowingIcon import com.vitorpamplona.amethyst.commons.ui.note.LikedIcon import com.vitorpamplona.amethyst.commons.ui.note.RepostedIcon +import com.vitorpamplona.amethyst.commons.ui.note.ScoreTagSmall import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange @@ -105,7 +107,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUse import com.vitorpamplona.amethyst.ui.components.CoreSecretMessage import com.vitorpamplona.amethyst.ui.components.ExpandableRichTextViewer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.navigation.routes.authorRouteFor import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 20c88f6e1c..e83291659c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -85,6 +85,7 @@ import com.vitorpamplona.amethyst.commons.resources.private_rumor_info_title import com.vitorpamplona.amethyst.commons.resources.private_rumor_mark import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.layouts.GenericRepostLayout import com.vitorpamplona.amethyst.commons.ui.layouts.NoteComposeLayout import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -117,7 +118,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observe import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeCommunityApprovalNeedStatus import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteModifications -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeEditDraftTo import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.DisplayZapSplits diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt index eb589ce309..c2c54ffb9d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt @@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.expand_relay_list import com.vitorpamplona.amethyst.commons.resources.relay_info import com.vitorpamplona.amethyst.commons.ui.components.ClickableBox +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.LargeRelayIconModifier @@ -70,7 +71,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.relayIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.ripple24dp import com.vitorpamplona.amethyst.commons.ui.theme.warningColorOnSecondSurface import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.util.setText import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt index 5cfa7ff7cd..3aa93de82f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt @@ -21,25 +21,18 @@ package com.vitorpamplona.amethyst.ui.note import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -47,21 +40,16 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.resources.Res -import com.vitorpamplona.amethyst.commons.resources.profile_image -import com.vitorpamplona.amethyst.commons.resources.profile_image_of_user import com.vitorpamplona.amethyst.commons.resources.unknown_author +import com.vitorpamplona.amethyst.commons.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.FollowingIcon +import com.vitorpamplona.amethyst.commons.ui.note.ScoreTag +import com.vitorpamplona.amethyst.commons.ui.note.UserPictureImage import com.vitorpamplona.amethyst.commons.ui.stringRes -import com.vitorpamplona.amethyst.commons.ui.theme.Font10SP -import com.vitorpamplona.amethyst.commons.ui.theme.Font6SP -import com.vitorpamplona.amethyst.commons.ui.theme.Font8SP -import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsScore import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo -import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser @@ -525,84 +513,6 @@ fun ScoreTag55Preview() { } } -@Composable -fun ScoreTag( - score: Int, - size: Dp, - modifier: Modifier, -) { - if (size > 34.dp) { - ScoreTagRegular(score, modifier) - } else if (size > 23.dp) { - ScoreTagSmall(score, modifier) - } else { - ScoreTagSmallest(score, modifier) - } -} - -@Composable -fun ScoreTagRegular( - score: Int, - modifier: Modifier, -) { - Text( - text = score.toString(), - color = Color.White, - fontWeight = FontWeight.Bold, - fontSize = Font10SP, - maxLines = 1, - overflow = TextOverflow.Clip, - lineHeight = Font10SP, - modifier = - modifier - .clip(SmallBorder) - .background(Color.Black) - .padding(horizontal = 4.dp, vertical = 0.dp), - ) -} - -@Composable -fun ScoreTagSmall( - score: Int, - modifier: Modifier, -) { - Text( - text = score.toString(), - color = Color.White, - fontWeight = FontWeight.Bold, - fontSize = Font8SP, - maxLines = 1, - overflow = TextOverflow.Clip, - lineHeight = Font8SP, - modifier = - modifier - .clip(SmallBorder) - .background(Color.Black) - .padding(horizontal = 3.dp, vertical = 0.dp), - ) -} - -@Composable -fun ScoreTagSmallest( - score: Int, - modifier: Modifier, -) { - Text( - text = score.toString(), - color = Color.White, - fontWeight = FontWeight.Bold, - fontSize = Font6SP, - maxLines = 1, - overflow = TextOverflow.Clip, - lineHeight = Font6SP, - modifier = - modifier - .clip(SmallBorder) - .background(Color.Black) - .padding(horizontal = 2.dp, vertical = 0.dp), - ) -} - @Composable fun WatchProfilePicture( baseUser: User, @@ -623,22 +533,12 @@ fun InnerUserPicture( modifier: Modifier, accountViewModel: AccountViewModel, ) { - val myImageModifier = - remember { - modifier.size(size).clip(shape = CircleShape) - } - - RobohashFallbackAsyncImage( - robot = userHex, - model = userPicture, - contentDescription = - if (userName != null) { - stringRes(id = Res.string.profile_image_of_user, userName) - } else { - stringRes(id = Res.string.profile_image) - }, - modifier = myImageModifier, - contentScale = ContentScale.Crop, + UserPictureImage( + userHex = userHex, + userPicture = userPicture, + userName = userName, + size = size, + modifier = modifier, loadProfilePicture = accountViewModel.settings.showProfilePictures(), loadRobohash = accountViewModel.settings.isNotPerformanceMode(), autoPlayGif = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Badge.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Badge.kt index 4522c97552..2ea85eed91 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Badge.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Badge.kt @@ -74,12 +74,12 @@ import com.vitorpamplona.amethyst.commons.resources.badge_awardees_label import com.vitorpamplona.amethyst.commons.resources.badge_untitled import com.vitorpamplona.amethyst.commons.resources.reject_badge import com.vitorpamplona.amethyst.commons.resources.unaccept_badge +import com.vitorpamplona.amethyst.commons.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size30dp import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonRow import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent -import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt index 8a5eb85fe9..8a95513673 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt @@ -76,6 +76,7 @@ import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for import com.vitorpamplona.amethyst.commons.resources.profile_image import com.vitorpamplona.amethyst.commons.resources.quick_action_share import com.vitorpamplona.amethyst.commons.resources.quick_action_share_browser_link +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder @@ -93,7 +94,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.components.RichTextViewer -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PodcastPeople.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PodcastPeople.kt index 5b25f5ecc1..9bd69eb2ae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PodcastPeople.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PodcastPeople.kt @@ -44,11 +44,11 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.podcast_hosts_and_guests +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp import com.vitorpamplona.amethyst.commons.ui.theme.grayText -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture import com.vitorpamplona.amethyst.ui.note.UsernameDisplay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RelayGroupMessage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RelayGroupMessage.kt index 67ff0aff42..aa62be4e9a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RelayGroupMessage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RelayGroupMessage.kt @@ -48,11 +48,11 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel import com.vitorpamplona.amethyst.commons.nip29RelayGroups.ui.RelayNameChip import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserName -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.buzz.workspace.buzzParticipants diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/apps/recommendations/ProfileAppRecommendationsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/apps/recommendations/ProfileAppRecommendationsScreen.kt index a56295a56c..9d462ebcf1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/apps/recommendations/ProfileAppRecommendationsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/apps/recommendations/ProfileAppRecommendationsScreen.kt @@ -67,6 +67,7 @@ import com.vitorpamplona.amethyst.commons.resources.profile_app_recommendations_ import com.vitorpamplona.amethyst.commons.resources.profile_app_recommendations_search import com.vitorpamplona.amethyst.commons.resources.profile_app_recommendations_search_empty import com.vitorpamplona.amethyst.commons.resources.select_list_to_filter +import com.vitorpamplona.amethyst.commons.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.commons.ui.note.ArrowBackIcon @@ -77,7 +78,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent -import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.navigation.topbars.FeedFilterSpinner import com.vitorpamplona.amethyst.ui.note.types.ByAuthorChip diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt index d68ba9f721..5667f0e30e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt @@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.model.backups.ReplaceableBackupConflic import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.util.toShortDisplay @@ -76,7 +77,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observe import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.ui.components.LoadNote import com.vitorpamplona.amethyst.ui.components.MyAsyncImage -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.UserPicture diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt index e1beb6c625..c8feeaa482 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictListViews.kt @@ -61,12 +61,12 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.backups.BackupEventType import com.vitorpamplona.amethyst.commons.model.backups.ReplaceableBackupConflict import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.util.toShortDisplay import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.LoadPublicChatChannel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/ProfileBadgesScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/ProfileBadgesScreen.kt index 5b09074883..0d3cbdf7fb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/ProfileBadgesScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/ProfileBadgesScreen.kt @@ -57,12 +57,12 @@ import com.vitorpamplona.amethyst.commons.resources.badge_untitled import com.vitorpamplona.amethyst.commons.resources.profile_badges_description import com.vitorpamplona.amethyst.commons.resources.profile_badges_empty import com.vitorpamplona.amethyst.commons.resources.profile_badges_title +import com.vitorpamplona.amethyst.commons.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent -import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/GeohashChatScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/GeohashChatScreen.kt index ce9df281b9..fb7b9de13f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/GeohashChatScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/geohashChat/GeohashChatScreen.kt @@ -64,12 +64,12 @@ import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.model.geohashChat.GeohashChatChannel import com.vitorpamplona.amethyst.commons.search.SearchSeed +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.navigation.topbars.SearchTopBarAction diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconEditor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconEditor.kt index f0fabf7766..faa029d834 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconEditor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/marmotGroup/MarmotGroupIconEditor.kt @@ -44,9 +44,9 @@ import com.vitorpamplona.amethyst.commons.resources.marmot_add_photo import com.vitorpamplona.amethyst.commons.resources.marmot_change_photo import com.vitorpamplona.amethyst.commons.resources.marmot_group_icon import com.vitorpamplona.amethyst.commons.resources.marmot_remove_photo +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.HexKey diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordHomeScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordHomeScreen.kt index 8df0607a89..a4300dae56 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordHomeScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordHomeScreen.kt @@ -67,13 +67,13 @@ import com.vitorpamplona.amethyst.commons.resources.concord_home_empty import com.vitorpamplona.amethyst.commons.resources.concord_home_title import com.vitorpamplona.amethyst.commons.resources.concord_member_count import com.vitorpamplona.amethyst.commons.resources.concord_show_all_channels +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserName -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.FabBottomBarPadded import com.vitorpamplona.amethyst.ui.note.timeAgo diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordMetadataForm.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordMetadataForm.kt index eec6288437..6dafebd7ce 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordMetadataForm.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/concord/ConcordMetadataForm.kt @@ -74,12 +74,12 @@ import com.vitorpamplona.amethyst.commons.resources.concord_create_title import com.vitorpamplona.amethyst.commons.resources.concord_edit_banner_hint import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details import com.vitorpamplona.amethyst.commons.resources.remove +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.MediumRelayIconModifier import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.util.setText import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt index 9fc763710b..da2bce20a3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt @@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.commons.chats.ui.LeaveChannelButton import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.profile_image +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.HeaderPictureModifier @@ -47,7 +48,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingChannel -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt index ae56c76d54..a5757f0ed1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt @@ -50,6 +50,7 @@ import com.vitorpamplona.amethyst.commons.resources.channel_image import com.vitorpamplona.amethyst.commons.resources.created_at import com.vitorpamplona.amethyst.commons.resources.groups_no_descriptor import com.vitorpamplona.amethyst.commons.resources.owner +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer @@ -60,7 +61,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observe import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingChannel import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.LoadNote -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt index 317783b3a6..531c6c4399 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt @@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.commons.chats.ui.JoinChannelButton import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.profile_image +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.HeaderPictureModifier @@ -50,7 +51,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingChannel import com.vitorpamplona.amethyst.ui.components.LoadNote -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.LikeReaction import com.vitorpamplona.amethyst.ui.note.ZapReaction import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt index 43ccd79d8d..8093f081eb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt @@ -94,6 +94,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_group_section_forums import com.vitorpamplona.amethyst.commons.resources.relay_tor_clearnet_action import com.vitorpamplona.amethyst.commons.resources.relay_tor_clearnet_body import com.vitorpamplona.amethyst.commons.resources.relay_tor_clearnet_title +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton import com.vitorpamplona.amethyst.commons.ui.pluralStringRes @@ -103,7 +104,6 @@ import com.vitorpamplona.amethyst.commons.util.sortedBySnapshot import com.vitorpamplona.amethyst.model.nip11RelayInfo.isRelaySignedRelayGroup import com.vitorpamplona.amethyst.model.nip11RelayInfo.looksLikeNonNip29Relay import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserName -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.UserPicture diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt index e6e0a4a720..e55e17f57c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupDiscoveryScreen.kt @@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_group_member_count import com.vitorpamplona.amethyst.commons.resources.relay_group_message_count_short_capped import com.vitorpamplona.amethyst.commons.resources.relay_group_relay_group_count import com.vitorpamplona.amethyst.commons.resources.select_list_to_filter +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.commons.ui.feeds.ScrollStateKeys import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel @@ -83,7 +84,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.warningColor import com.vitorpamplona.amethyst.model.nip11RelayInfo.WarmNip11 import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserName -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupParentPicker.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupParentPicker.kt index c0823de922..627c5face7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupParentPicker.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupParentPicker.kt @@ -75,12 +75,12 @@ import com.vitorpamplona.amethyst.commons.resources.relay_group_parent_pick_titl import com.vitorpamplona.amethyst.commons.resources.relay_group_parent_search import com.vitorpamplona.amethyst.commons.resources.relay_group_parent_top_level_option import com.vitorpamplona.amethyst.commons.resources.relay_group_section_structure +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.pluralStringRes import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.util.sortedBySnapshot import com.vitorpamplona.amethyst.model.nip11RelayInfo.isRelaySignedRelayGroup import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupCardWarmupSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsOnRelaySubscription diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt index 96e70b3588..ec0e05f483 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt @@ -38,11 +38,11 @@ import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChann import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.profile_image +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.HeaderPictureModifier import com.vitorpamplona.amethyst.commons.ui.theme.Size34dp -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ChatFileUploadDialog diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt index 9772cc57ff..43bc9c72c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt @@ -95,6 +95,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_group_server_label import com.vitorpamplona.amethyst.commons.resources.remove_from_messages import com.vitorpamplona.amethyst.commons.resources.unmute_notifications import com.vitorpamplona.amethyst.commons.resources.unpin_conversation +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.HeaderPill import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -113,7 +114,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observe import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteHasEvent import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderByParentFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserName -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.layouts.ChatHeaderLayout import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.BlankNote diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/badges/DisplayBadges.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/badges/DisplayBadges.kt index 40a1adaf7a..aea887d665 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/badges/DisplayBadges.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/badges/DisplayBadges.kt @@ -70,12 +70,12 @@ import com.vitorpamplona.amethyst.commons.resources.badge_award_image_for import com.vitorpamplona.amethyst.commons.resources.badge_untitled import com.vitorpamplona.amethyst.commons.resources.profile_badges_header import com.vitorpamplona.amethyst.commons.resources.profile_badges_title +import com.vitorpamplona.amethyst.commons.ui.components.RobohashAsyncImage +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent -import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRScreen.kt index c733ea070f..4321485aa8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRScreen.kt @@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.profile_image import com.vitorpamplona.amethyst.commons.resources.scan_qr +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton @@ -64,7 +65,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp import com.vitorpamplona.amethyst.commons.ui.theme.largeProfilePictureModifier import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.ui.note.ObserveAndDisplayNIP05 import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relayauth/RelayAuthSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relayauth/RelayAuthSettingsScreen.kt index b867cb022e..83deb6a5f8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relayauth/RelayAuthSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relayauth/RelayAuthSettingsScreen.kt @@ -109,6 +109,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_auth_session_undo_bloc import com.vitorpamplona.amethyst.commons.resources.relay_auth_settings_title import com.vitorpamplona.amethyst.commons.resources.relay_auth_undo import com.vitorpamplona.amethyst.commons.resources.relay_info +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -116,7 +117,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.MediumRelayIconModifier import com.vitorpamplona.amethyst.commons.ui.theme.RelayIconFilter import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.relayAuthPurposeLabelRes -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.timeAgo import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsDivider diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt index f513a35aed..42fcf0d8e5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/subscriptions/ActiveSubscriptionsScreen.kt @@ -87,6 +87,7 @@ import com.vitorpamplona.amethyst.commons.resources.active_subs_title import com.vitorpamplona.amethyst.commons.resources.active_subs_unattributed import com.vitorpamplona.amethyst.commons.resources.active_subs_untagged import com.vitorpamplona.amethyst.commons.resources.marmot_group_fallback_name +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.theme.RelayIconFilter @@ -94,7 +95,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.allGoodColor import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText import com.vitorpamplona.amethyst.commons.ui.theme.warningColor import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo -import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.creators.location.LoadCityName import com.vitorpamplona.amethyst.ui.pluralStringRes import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/AnimatedMediaUrl.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/AnimatedMediaUrl.kt new file mode 100644 index 0000000000..8f3a251674 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/AnimatedMediaUrl.kt @@ -0,0 +1,30 @@ +/* + * 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.commons.richtext + +/** True for GIF/AVIF urls, which may animate and so bypass still-image pipelines like the avatar thumbnail cache. */ +fun isAnimatedMediaUrl(url: String): Boolean = + url.endsWith(".gif", ignoreCase = true) || + url.contains(".gif?", ignoreCase = true) || + url.contains(".gif#", ignoreCase = true) || + url.endsWith(".avif", ignoreCase = true) || + url.contains(".avif?", ignoreCase = true) || + url.contains(".avif#", ignoreCase = true) diff --git a/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.android.kt b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.android.kt new file mode 100644 index 0000000000..f93981c56b --- /dev/null +++ b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.android.kt @@ -0,0 +1,43 @@ +/* + * 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.commons.ui.components + +import android.graphics.drawable.Animatable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalContext +import coil3.Image +import coil3.asDrawable + +@Composable +internal actual fun AnimatedImageAutoPlay( + image: Image?, + autoPlay: Boolean, +) { + val resources = LocalContext.current.resources + val drawable = image?.asDrawable(resources) + + LaunchedEffect(drawable, autoPlay) { + if (drawable is Animatable) { + if (autoPlay) drawable.start() else drawable.stop() + } + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/user/UserMetadataObservers.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/user/UserMetadataObservers.kt index c1d20a41d9..f2dd189808 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/user/UserMetadataObservers.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/user/UserMetadataObservers.kt @@ -62,8 +62,13 @@ fun observeUserPicture( user: User, userFinder: UserFinderFilterAssembler, account: UserFinderAccount, + // Callers that already hold a subscription for the same user (e.g. an author avatar that + // also observes the contact-card score) pass false to skip a redundant relay subscription. + subscribe: Boolean = true, ): State { - UserFinderFilterAssemblerSubscription(user, account, userFinder) + if (subscribe) { + UserFinderFilterAssemblerSubscription(user, account, userFinder) + } val flow = remember(user) { diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ScoreTag.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ScoreTag.kt new file mode 100644 index 0000000000..bb69d45d37 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ScoreTag.kt @@ -0,0 +1,119 @@ +/* + * 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.commons.ui.note + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.commons.ui.theme.Font10SP +import com.vitorpamplona.amethyst.commons.ui.theme.Font6SP +import com.vitorpamplona.amethyst.commons.ui.theme.Font8SP +import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder + +/** + * Trust-score chip drawn over the bottom of an avatar, sized to the avatar: regular above 34dp, + * small above 23dp, smallest below. + */ +@Composable +fun ScoreTag( + score: Int, + size: Dp, + modifier: Modifier, +) { + if (size > 34.dp) { + ScoreTagRegular(score, modifier) + } else if (size > 23.dp) { + ScoreTagSmall(score, modifier) + } else { + ScoreTagSmallest(score, modifier) + } +} + +@Composable +fun ScoreTagRegular( + score: Int, + modifier: Modifier, +) { + Text( + text = score.toString(), + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = Font10SP, + maxLines = 1, + overflow = TextOverflow.Clip, + lineHeight = Font10SP, + modifier = + modifier + .clip(SmallBorder) + .background(Color.Black) + .padding(horizontal = 4.dp, vertical = 0.dp), + ) +} + +@Composable +fun ScoreTagSmall( + score: Int, + modifier: Modifier, +) { + Text( + text = score.toString(), + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = Font8SP, + maxLines = 1, + overflow = TextOverflow.Clip, + lineHeight = Font8SP, + modifier = + modifier + .clip(SmallBorder) + .background(Color.Black) + .padding(horizontal = 3.dp, vertical = 0.dp), + ) +} + +@Composable +fun ScoreTagSmallest( + score: Int, + modifier: Modifier, +) { + Text( + text = score.toString(), + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = Font6SP, + maxLines = 1, + overflow = TextOverflow.Clip, + lineHeight = Font6SP, + modifier = + modifier + .clip(SmallBorder) + .background(Color.Black) + .padding(horizontal = 2.dp, vertical = 0.dp), + ) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RobohashAsyncImage.kt similarity index 88% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt rename to commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RobohashAsyncImage.kt index dcfce38a90..c1725fb28b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RobohashAsyncImage.kt @@ -18,15 +18,13 @@ * 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.components +package com.vitorpamplona.amethyst.commons.ui.components -import android.graphics.drawable.Animatable import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -38,9 +36,8 @@ import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import coil3.asDrawable import coil3.compose.AsyncImagePainter +import coil3.compose.LocalPlatformContext import coil3.compose.SubcomposeAsyncImage import coil3.compose.SubcomposeAsyncImageContent import coil3.network.NetworkHeaders @@ -48,12 +45,22 @@ import coil3.network.httpHeaders import coil3.request.ImageRequest import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.icons.symbols.rememberMaterialSymbolPainter +import com.vitorpamplona.amethyst.commons.richtext.isAnimatedMediaUrl import com.vitorpamplona.amethyst.commons.robohash.CachedRobohash import com.vitorpamplona.amethyst.commons.service.http.LocalBlossomCacheRedirectInterceptor -import com.vitorpamplona.amethyst.commons.ui.components.ProfilePictureUrl -import com.vitorpamplona.amethyst.commons.ui.components.forwardingPainter import com.vitorpamplona.amethyst.commons.ui.theme.isLight import com.vitorpamplona.amethyst.commons.ui.theme.onBackgroundColorFilter +import coil3.Image as CoilImage + +/** + * Plays or pauses an animated avatar (GIF/AVIF) once Coil has decoded it. Android drives the + * decoded `Animatable` drawable; desktop has no such drawable, so it is a no-op there. + */ +@Composable +internal expect fun AnimatedImageAutoPlay( + image: CoilImage?, + autoPlay: Boolean, +) @Composable fun RobohashAsyncImage( @@ -115,7 +122,6 @@ fun RobohashFallbackAsyncImage( ) } - val resources = LocalContext.current.resources SubcomposeAsyncImage( // The thumbnail-cache fetcher behind ProfilePictureUrl delegates to Coil's http-only // NetworkFetcher, so a LOCAL model (e.g. a decrypted Concord community icon cached at @@ -136,14 +142,7 @@ fun RobohashFallbackAsyncImage( filterQuality = filterQuality, ) { val state by painter.state.collectAsState() - val successState = state as? AsyncImagePainter.State.Success - val drawable = successState?.result?.image?.asDrawable(resources) - - LaunchedEffect(drawable, autoPlayGif) { - if (drawable is Animatable) { - if (autoPlayGif) drawable.start() else drawable.stop() - } - } + AnimatedImageAutoPlay((state as? AsyncImagePainter.State.Success)?.result?.image, autoPlayGif) when (state) { is AsyncImagePainter.State.Success -> SubcomposeAsyncImageContent() @@ -194,7 +193,6 @@ fun GifProfilePicture( loadRobohash: Boolean, autoPlay: Boolean, ) { - val resources = LocalContext.current.resources val fallbackPainter = if (loadRobohash) { rememberVectorPainter(image = CachedRobohash.get(userHex, MaterialTheme.colorScheme.isLight)) @@ -205,7 +203,7 @@ fun GifProfilePicture( ) } - val context = LocalContext.current + val context = LocalPlatformContext.current // Animated avatars skip ProfilePictureFetcher (its thumbnail cache would flatten them), so // they carry the profile-picture marker themselves for the local Blossom cache bridge. val model = @@ -229,14 +227,7 @@ fun GifProfilePicture( modifier = Modifier.fillMaxSize(), ) { val state by painter.state.collectAsState() - val successState = state as? AsyncImagePainter.State.Success - val drawable = successState?.result?.image?.asDrawable(resources) - - LaunchedEffect(drawable, autoPlay) { - if (drawable is Animatable) { - if (autoPlay) drawable.start() else drawable.stop() - } - } + AnimatedImageAutoPlay((state as? AsyncImagePainter.State.Success)?.result?.image, autoPlay) when (state) { is AsyncImagePainter.State.Success -> { diff --git a/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/note/UserPictureImage.kt b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/note/UserPictureImage.kt new file mode 100644 index 0000000000..b3234d1f08 --- /dev/null +++ b/commonsUI/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/ui/note/UserPictureImage.kt @@ -0,0 +1,73 @@ +/* + * 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.commons.ui.note + +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.Dp +import com.vitorpamplona.amethyst.commons.resources.Res +import com.vitorpamplona.amethyst.commons.resources.profile_image +import com.vitorpamplona.amethyst.commons.resources.profile_image_of_user +import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage +import com.vitorpamplona.amethyst.commons.ui.stringRes + +/** + * A user's round avatar: their picture when [loadProfilePicture] allows it, otherwise (or while + * loading / on error) their robohash, or a plain face icon when [loadRobohash] is off. + * Animated pictures play only when [autoPlayGif]. + */ +@Composable +fun UserPictureImage( + userHex: String, + userPicture: String?, + userName: String?, + size: Dp, + modifier: Modifier, + loadProfilePicture: Boolean, + loadRobohash: Boolean, + autoPlayGif: Boolean, +) { + val myImageModifier = + remember { + modifier.size(size).clip(shape = CircleShape) + } + + RobohashFallbackAsyncImage( + robot = userHex, + model = userPicture, + contentDescription = + if (userName != null) { + stringRes(id = Res.string.profile_image_of_user, userName) + } else { + stringRes(id = Res.string.profile_image) + }, + modifier = myImageModifier, + contentScale = ContentScale.Crop, + loadProfilePicture = loadProfilePicture, + loadRobohash = loadRobohash, + autoPlayGif = autoPlayGif, + ) +} diff --git a/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.jvm.kt b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.jvm.kt new file mode 100644 index 0000000000..3edd5be51c --- /dev/null +++ b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/AnimatedImageAutoPlay.jvm.kt @@ -0,0 +1,32 @@ +/* + * 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.commons.ui.components + +import androidx.compose.runtime.Composable +import coil3.Image + +@Composable +internal actual fun AnimatedImageAutoPlay( + image: Image?, + autoPlay: Boolean, +) { + // Desktop decodes animated avatars without a controllable drawable; nothing to toggle. +}