mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
refactor(commonsUI): move the remaining movable layouts, feed helpers and link components
Most of the sweep's "Batch 4" had already landed in other PRs; this moves what was left that had no real blocker. - NewItemsBubble -> commonsUI components. - ChatHeaderLayout, LeftPictureLayout, listItem/SlimListItemLayout -> commonsUI layouts. Their @Previews use app drawables, TimeAgo and TextCount, so the previews stay in the app as *Preview.kt files. - WatchScrollToTop (list/grid/pager) -> commonsUI feeds; the CardFeedContentState overload stays in the app. The app's StickToTopOnPrepend was a copy of the commons one and is deleted; the commons one now collects with collectAsStateWithLifecycle, as the app copy did. - ChannelFeedState, ChannelFeedContentState -> commons feeds (headless). checkNotInMainThread() becomes LocalCache.appHost.assertNotMainThread(), which Android wires to the same check. - ClickableBuzzInviteLink, ClickableConcordInviteLink, ClickableRelayGroupLink, ClickableRelayUrl, OutlinedThinPaddingTextField -> commonsUI components. - The app's ClickableEmail/ClickablePhone are deleted in favour of the commons ones, which open mailto:/tel: through LocalUriHandler rather than ACTION_SEND/ACTION_DIAL intents. The commons ClickablePhone is new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjQQN9CgWHVtNCSWqnKoqY
This commit is contained in:
@@ -78,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_create_and_add_inv
|
||||
import com.vitorpamplona.amethyst.commons.resources.lightning_invoice
|
||||
import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar
|
||||
@@ -98,7 +99,6 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.uploads.ImageVideoDescription
|
||||
|
||||
@@ -52,6 +52,12 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextViewerState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.SecretEmoji
|
||||
import com.vitorpamplona.amethyst.commons.richtext.Segment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBuzzInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableConcordInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickablePhone
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayGroupLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayUrl
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NowhereLinkCard
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextInteractions
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
@Composable
|
||||
fun ClickableEmail(email: String) {
|
||||
val stripped = email.replaceFirst("mailto:", "")
|
||||
val context = LocalContext.current
|
||||
|
||||
ClickableTextPrimary(
|
||||
text = stripped,
|
||||
onClick = { runCatching { context.sendMail(stripped) } },
|
||||
)
|
||||
}
|
||||
|
||||
fun Context.sendMail(
|
||||
to: String,
|
||||
subject: String? = null,
|
||||
) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
intent.type = "vnd.android.cursor.item/email" // or "message/rfc822"
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
|
||||
if (subject != null) {
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||
}
|
||||
startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// TODO: Handle case where no email app is available
|
||||
} catch (t: Throwable) {
|
||||
if (t is CancellationException) throw t
|
||||
// TODO: Handle potential other type of exceptions
|
||||
}
|
||||
}
|
||||
@@ -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.ClickableConcordInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
|
||||
@@ -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.ClickableRelayGroupLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
|
||||
@@ -102,6 +102,12 @@ import com.vitorpamplona.amethyst.commons.richtext.Segment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.VideoSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBuzzInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableConcordInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickablePhone
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayGroupLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayUrl
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NowhereLinkCard
|
||||
|
||||
@@ -35,6 +35,8 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyGridState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyListState
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
|
||||
@@ -21,52 +21,11 @@
|
||||
package com.vitorpamplona.amethyst.ui.feeds
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CardFeedContentState
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
feedContentState: FeedContentState,
|
||||
listState: LazyListState,
|
||||
) {
|
||||
val scrollToTop by feedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && feedContentState.scrollToTopPending) {
|
||||
listState.scrollToItem(index = 0)
|
||||
feedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
feedContentState: FeedContentState,
|
||||
listState: LazyGridState,
|
||||
) {
|
||||
val scrollToTop by feedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && feedContentState.scrollToTopPending) {
|
||||
listState.scrollToItem(index = 0)
|
||||
feedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
@@ -82,166 +41,3 @@ fun WatchScrollToTop(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
videoFeedContentState: FeedContentState,
|
||||
pagerState: PagerState,
|
||||
) {
|
||||
val scrollToTop by videoFeedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && videoFeedContentState.scrollToTopPending) {
|
||||
pagerState.scrollToPage(page = 0)
|
||||
videoFeedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the user pinned to index 0 when new items prepend to a feed, but
|
||||
* only if they were already at the very top right before the update.
|
||||
*
|
||||
* Why this is non-trivial: every feed uses stable `key = item.idHex` in
|
||||
* its lazy list, which makes Compose preserve the visual anchor across
|
||||
* data changes. When N items prepend, the user's previously-visible
|
||||
* top item is still on screen but its index is now N — so
|
||||
* `firstVisibleItemIndex` shifts from 0 to N without any user gesture.
|
||||
* A naive `if (firstVisibleItemIndex <= 1) scrollToItem(0)` check inside
|
||||
* `LaunchedEffect(items.firstOrNull())` therefore fails as soon as more
|
||||
* than one item arrives in the same batch.
|
||||
*
|
||||
* The trick: track "was at top" continuously via [snapshotFlow], but
|
||||
* only flip it from true → false when [LazyListState.isScrollInProgress]
|
||||
* is true (i.e. the user is actively scrolling). Compose's keyed-item
|
||||
* shift after a data update does not set that flag — only real touch
|
||||
* gestures and `animate*` calls do — so data-driven index shifts can
|
||||
* never poison the cached value. When [firstItemKey] changes (head of
|
||||
* the list moved), if the cached value is still true, snap back to 0
|
||||
* with an instant (non-animated) scroll so the prepend appears as
|
||||
* in-place growth rather than a visible jump-then-scroll.
|
||||
*
|
||||
* Most callers should not invoke this directly: [SaveableFeedContentState],
|
||||
* [SaveableGridFeedContentState], and the analogous wrappers in
|
||||
* `ui/screen/FeedView.kt` already apply auto-stick to every feed they
|
||||
* own. Invoke the explicit overload only when the listState is
|
||||
* constructed outside one of those wrappers, or when the key that
|
||||
* should trigger the snap is not the default `items.list[0].idHex`
|
||||
* (e.g. notifications, chats, or feeds keyed on something other than a
|
||||
* Note's hex id).
|
||||
*/
|
||||
@Composable
|
||||
fun StickToTopOnPrepend(
|
||||
listState: LazyListState,
|
||||
firstItemKey: Any?,
|
||||
) {
|
||||
stickToTopOnPrepend(
|
||||
stateKey = listState,
|
||||
firstItemKey = firstItemKey,
|
||||
initialAtTop = {
|
||||
listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0
|
||||
},
|
||||
sampler = {
|
||||
snapshotFlow {
|
||||
listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0
|
||||
}
|
||||
},
|
||||
isScrollInProgress = { listState.isScrollInProgress },
|
||||
firstVisibleItemIndex = { listState.firstVisibleItemIndex },
|
||||
scrollToTop = { listState.scrollToItem(0) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StickToTopOnPrepend(
|
||||
gridState: LazyGridState,
|
||||
firstItemKey: Any?,
|
||||
) {
|
||||
stickToTopOnPrepend(
|
||||
stateKey = gridState,
|
||||
firstItemKey = firstItemKey,
|
||||
initialAtTop = {
|
||||
gridState.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0
|
||||
},
|
||||
sampler = {
|
||||
snapshotFlow {
|
||||
gridState.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0
|
||||
}
|
||||
},
|
||||
isScrollInProgress = { gridState.isScrollInProgress },
|
||||
firstVisibleItemIndex = { gridState.firstVisibleItemIndex },
|
||||
scrollToTop = { gridState.scrollToItem(0) },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-stick wired straight to a [FeedContentState]: derives the head
|
||||
* key from `feedContent → Loaded.feed → list.firstOrNull()?.idHex` so
|
||||
* callers don't have to collect the inner feed flow themselves. Used
|
||||
* by the Saveable* wrappers; suitable for any Note-keyed feed.
|
||||
*/
|
||||
@Composable
|
||||
fun StickToTopOnPrepend(
|
||||
feedContentState: FeedContentState,
|
||||
listState: LazyListState,
|
||||
) {
|
||||
StickToTopOnPrepend(listState, rememberFirstItemIdHex(feedContentState))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StickToTopOnPrepend(
|
||||
feedContentState: FeedContentState,
|
||||
gridState: LazyGridState,
|
||||
) {
|
||||
StickToTopOnPrepend(gridState, rememberFirstItemIdHex(feedContentState))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Composable
|
||||
private fun rememberFirstItemIdHex(feedContentState: FeedContentState): String? {
|
||||
val flow =
|
||||
remember(feedContentState) {
|
||||
feedContentState.feedContent.flatMapLatest { state ->
|
||||
when (state) {
|
||||
is FeedState.Loaded -> state.feed.map { it.list.firstOrNull()?.idHex }
|
||||
else -> flowOf(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
val key by flow.collectAsStateWithLifecycle(initialValue = null)
|
||||
return key
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun stickToTopOnPrepend(
|
||||
stateKey: Any,
|
||||
firstItemKey: Any?,
|
||||
initialAtTop: () -> Boolean,
|
||||
sampler: () -> Flow<Boolean>,
|
||||
isScrollInProgress: () -> Boolean,
|
||||
firstVisibleItemIndex: () -> Int,
|
||||
scrollToTop: suspend () -> Unit,
|
||||
) {
|
||||
// Plain holder instead of mutableStateOf — we only read this inside
|
||||
// effects, never in composition, so we don't need snapshot tracking.
|
||||
// Seed from the actual restored scroll position: when the user returns
|
||||
// to a feed via rememberForeverLazyListState, the saved offset is
|
||||
// already in place, and a hardcoded `true` would mis-snap them to 0.
|
||||
val wasAtTop = remember(stateKey) { booleanArrayOf(initialAtTop()) }
|
||||
|
||||
LaunchedEffect(stateKey) {
|
||||
sampler().collect { atTop ->
|
||||
if (atTop) {
|
||||
wasAtTop[0] = true
|
||||
} else if (isScrollInProgress()) {
|
||||
wasAtTop[0] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(firstItemKey) {
|
||||
if (firstItemKey != null && wasAtTop[0] && firstVisibleItemIndex() > 0) {
|
||||
scrollToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-50
@@ -20,16 +20,10 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.layouts
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Text
|
||||
@@ -38,20 +32,19 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_banner
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NewItemsBubble
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.NewItemsBubble
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
private const val PREVIEW_AUTHOR = "This is my author"
|
||||
@@ -136,43 +129,3 @@ fun ChannelNamePreview() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ChatHeaderLayout(
|
||||
channelPicture: @Composable () -> Unit,
|
||||
firstRow: @Composable RowScope.() -> Unit,
|
||||
secondRow: @Composable RowScope.() -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(Size55Modifier) { channelPicture() }
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
firstRow()
|
||||
}
|
||||
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
secondRow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-52
@@ -21,18 +21,11 @@
|
||||
package com.vitorpamplona.amethyst.ui.layouts
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
@@ -43,10 +36,10 @@ import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_banner
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LikeIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ZappedIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
|
||||
@@ -104,47 +97,3 @@ fun LeftPictureLayoutPreviewCard() {
|
||||
onBottomRow = { Text("This is my Moderator List") },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LeftPictureLayout(
|
||||
onImage: @Composable () -> Unit,
|
||||
onTitleRow: @Composable RowScope.() -> Unit,
|
||||
onDescription: @Composable () -> Unit,
|
||||
onBottomRow: @Composable RowScope.() -> Unit,
|
||||
imageFraction: Float = 0.25f,
|
||||
) {
|
||||
Row(Modifier.aspectRatio(ratio = 1 / imageFraction)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(imageFraction).aspectRatio(ratio = 1f),
|
||||
) {
|
||||
onImage()
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
onTitleRow()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().weight(1f),
|
||||
) {
|
||||
onDescription()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
onBottomRow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.layouts.listItem
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_banner
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NewItemsBubble
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
private const val PREVIEW_AUTHOR = "This is my author"
|
||||
private const val PREVIEW_MESSAGE = "This is a message from this person"
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun ChannelNamePreview() {
|
||||
ThemeComparisonColumn {
|
||||
Column {
|
||||
ChatHeaderLayout(
|
||||
channelPicture = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 1),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
},
|
||||
firstRow = {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
},
|
||||
secondRow = {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
NewItemsBubble()
|
||||
},
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
SlimListItem(
|
||||
headlineContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
NewItemsBubble()
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 2),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Size55Modifier,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
NewItemsBubble()
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 2),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Size55Modifier,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.resources.follow
|
||||
import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_follow
|
||||
import com.vitorpamplona.amethyst.commons.resources.login_with_a_private_key_to_be_able_to_unfollow
|
||||
import com.vitorpamplona.amethyst.commons.resources.read_only_user
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ShowUserButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
@@ -51,7 +52,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAboutMe
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowing
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
|
||||
+1
-1
@@ -88,6 +88,7 @@ import com.vitorpamplona.amethyst.commons.resources.discover_follows
|
||||
import com.vitorpamplona.amethyst.commons.resources.follow_sets
|
||||
import com.vitorpamplona.amethyst.commons.resources.num_selected
|
||||
import com.vitorpamplona.amethyst.commons.resources.select_all
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
@@ -95,7 +96,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size24dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.warningColor
|
||||
import com.vitorpamplona.amethyst.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList
|
||||
|
||||
+1
-1
@@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.user_suggestion_in_this_chat
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Font14SP
|
||||
@@ -60,7 +61,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.NIP05IconSize
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.nip05
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.UserSearchDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.ObserveAndRenderNIP05VerifiedSymbol
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
|
||||
+1
-1
@@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.commons.resources.zap_split_explainer
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_split_search_and_add_user
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_split_search_and_add_user_placeholder
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_split_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.IZapField
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.creators.zapsplits.ZapSplitIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -55,7 +56,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.actions.MentionPreservingInputTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagOutputTransformation
|
||||
import com.vitorpamplona.amethyst.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -35,14 +35,14 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyGridState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyListState
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.FeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedLoaded
|
||||
import com.vitorpamplona.amethyst.ui.feeds.PrefetchLoadedFeedMedia
|
||||
import com.vitorpamplona.amethyst.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -35,11 +35,11 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
|
||||
+1
-1
@@ -22,13 +22,13 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.content.ComponentCallbacks2
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.feeds.ChannelFeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ChannelFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.dal.ArticlesFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.dal.BadgesFeedFilter
|
||||
|
||||
+1
-1
@@ -64,13 +64,13 @@ import com.vitorpamplona.amethyst.commons.resources.calendar_export_event
|
||||
import com.vitorpamplona.amethyst.commons.resources.calendar_export_share_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyListState
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
|
||||
+1
-1
@@ -47,11 +47,11 @@ import com.vitorpamplona.amethyst.commons.resources.calendar_empty_feed_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.calendar_section_past
|
||||
import com.vitorpamplona.amethyst.commons.resources.calendar_section_upcoming
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
|
||||
+2
-7
@@ -106,7 +106,9 @@ 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.NewItemsBubble
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.HeaderPill
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
@@ -118,7 +120,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.newItemBubbleModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.model.buzz.toMembershipNotice
|
||||
import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
|
||||
@@ -126,7 +127,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.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.BlankNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContentOrNull
|
||||
@@ -1377,8 +1377,3 @@ private fun TimeAgo(channelLastTime: Long?) {
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NewItemsBubble() {
|
||||
Box(MaterialTheme.colorScheme.newItemBubbleModifier)
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.Mute
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.chat_about_topic
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfTopPadding
|
||||
@@ -61,7 +62,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner
|
||||
import com.vitorpamplona.amethyst.ui.note.Gallery
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
|
||||
+1
-1
@@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.Mute
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.community_about_topic
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfTopPadding
|
||||
@@ -63,7 +64,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner
|
||||
import com.vitorpamplona.amethyst.ui.note.Gallery
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
|
||||
+1
-1
@@ -41,6 +41,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfTopPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.RowColSpacing5dp
|
||||
@@ -51,7 +52,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.nip05
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
|
||||
+2
-2
@@ -46,12 +46,12 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyGridState
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+2
-2
@@ -61,6 +61,8 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.feeds.ChannelFeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.ChannelFeedState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedState
|
||||
import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
|
||||
@@ -96,8 +98,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.TabRowHeight
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonRow
|
||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ChannelFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ChannelFeedState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
|
||||
+1
-1
@@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RelayReachCursor
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RelayReachDetailDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RelayReachMarkers
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.CloseIcon
|
||||
@@ -73,7 +74,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier
|
||||
import com.vitorpamplona.amethyst.logTime
|
||||
import com.vitorpamplona.amethyst.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.ui.note.BadgeCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.MessageSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.MultiSetCompose
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.commons.resources.notification_tab_following
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.PagerStateKeys
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverLazyListState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
|
||||
+2
-2
@@ -40,12 +40,12 @@ import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.feeds.PrefetchLoadedFeedMedia
|
||||
import com.vitorpamplona.amethyst.ui.feeds.StickToTopOnPrepend
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
|
||||
+1
-1
@@ -35,12 +35,12 @@ import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.UserActionOptions
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
|
||||
+1
-1
@@ -122,6 +122,7 @@ import com.vitorpamplona.amethyst.commons.resources.relay_management_tab_pubkeys
|
||||
import com.vitorpamplona.amethyst.commons.resources.relay_management_tab_settings
|
||||
import com.vitorpamplona.amethyst.commons.resources.relay_management_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.search_and_add_a_user
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ClearTextIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -133,7 +134,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SmallBorder
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.nip05
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.UserSearchDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem
|
||||
import com.vitorpamplona.amethyst.ui.note.AboutDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.ObserveAndRenderNIP05VerifiedSymbol
|
||||
|
||||
+6
-8
@@ -18,26 +18,24 @@
|
||||
* 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.feeds
|
||||
package com.vitorpamplona.amethyst.commons.feeds
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.vitorpamplona.amethyst.commons.feeds.AdditiveComplexFeedFilter
|
||||
import com.vitorpamplona.amethyst.commons.feeds.InvalidatableContent
|
||||
import com.vitorpamplona.amethyst.commons.feeds.LoadedFeedState
|
||||
import com.vitorpamplona.amethyst.commons.model.Channel
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.service.BundledInsert
|
||||
import com.vitorpamplona.amethyst.commons.service.BundledUpdate
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
|
||||
import com.vitorpamplona.amethyst.commons.util.equalImmutableLists
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.flattenToSet
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -75,7 +73,7 @@ class ChannelFeedContentState(
|
||||
}
|
||||
|
||||
fun refreshSuspended() {
|
||||
checkNotInMainThread()
|
||||
LocalCache.appHost.assertNotMainThread()
|
||||
|
||||
isRefreshing.value = true
|
||||
try {
|
||||
@@ -178,7 +176,7 @@ class ChannelFeedContentState(
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
Log.d("Init") { "OnCleared: ${this.javaClass.simpleName}" }
|
||||
Log.d("Init") { "OnCleared: ${this::class.simpleName}" }
|
||||
bundlerInsert.cancel()
|
||||
bundler.cancel()
|
||||
}
|
||||
+1
-2
@@ -18,10 +18,9 @@
|
||||
* 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.feeds
|
||||
package com.vitorpamplona.amethyst.commons.feeds
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.feeds.LoadedFeedState
|
||||
import com.vitorpamplona.amethyst.commons.model.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
+24
@@ -91,3 +91,27 @@ fun ClickableEmail(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A primary-colored clickable phone number that opens the platform dialer via
|
||||
* [LocalUriHandler] (`tel:`). Strips a leading `tel:` from the display text.
|
||||
* [underline] + hover cursor are the Desktop mouse-first affordances.
|
||||
*/
|
||||
@Composable
|
||||
fun ClickablePhone(
|
||||
number: String,
|
||||
modifier: Modifier = Modifier,
|
||||
underline: Boolean = false,
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val display = remember(number) { number.removePrefix("tel:") }
|
||||
Text(
|
||||
text = display,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
textDecoration = if (underline) TextDecoration.Underline else null,
|
||||
modifier =
|
||||
modifier.pointerHoverIcon(PointerIcon.Hand).clickable {
|
||||
runCatching { uriHandler.openUri("tel:$display") }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
+7
-22
@@ -18,30 +18,15 @@
|
||||
* 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.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.newItemBubbleModifier
|
||||
|
||||
/** The small dot that marks a row with unread items. */
|
||||
@Composable
|
||||
fun ClickablePhone(phone: String) {
|
||||
val context = LocalContext.current
|
||||
|
||||
ClickableTextPrimary(
|
||||
text = phone,
|
||||
onClick = { context.dial(phone) },
|
||||
)
|
||||
}
|
||||
|
||||
fun Context.dial(phone: String) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", phone, null))
|
||||
startActivity(intent)
|
||||
} catch (t: Throwable) {
|
||||
// TODO: Handle potential exceptions
|
||||
}
|
||||
fun NewItemsBubble() {
|
||||
Box(MaterialTheme.colorScheme.newItemBubbleModifier)
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
+10
-7
@@ -24,10 +24,10 @@ import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||
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
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedState
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -59,11 +59,14 @@ import kotlinx.coroutines.flow.map
|
||||
* with an instant (non-animated) scroll so the prepend appears as
|
||||
* in-place growth rather than a visible jump-then-scroll.
|
||||
*
|
||||
* Commons port of `amethyst/.../WatchScrollToTop.kt` so Desktop and any
|
||||
* other multiplatform front-end can use the same auto-stick behavior.
|
||||
* Uses plain `collectAsState` instead of the Android-only
|
||||
* `collectAsStateWithLifecycle` — equivalent here because the effect's
|
||||
* lifecycle is already bound to composition via `LaunchedEffect`.
|
||||
* Most callers should not invoke this directly: `SaveableFeedContentState`,
|
||||
* `SaveableGridFeedContentState`, and the analogous wrappers in the app's
|
||||
* `ui/screen/FeedView.kt` already apply auto-stick to every feed they
|
||||
* own. Invoke the explicit overload only when the listState is
|
||||
* constructed outside one of those wrappers, or when the key that
|
||||
* should trigger the snap is not the default `items.list[0].idHex`
|
||||
* (e.g. notifications, chats, or feeds keyed on something other than a
|
||||
* Note's hex id).
|
||||
*/
|
||||
@Composable
|
||||
fun StickToTopOnPrepend(
|
||||
@@ -142,7 +145,7 @@ private fun rememberFirstItemIdHex(feedContentState: FeedContentState): String?
|
||||
}
|
||||
}
|
||||
}
|
||||
val key by flow.collectAsState(initial = null)
|
||||
val key by flow.collectAsStateWithLifecycle(initialValue = null)
|
||||
return key
|
||||
}
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.feeds
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.feeds.FeedContentState
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
feedContentState: FeedContentState,
|
||||
listState: LazyListState,
|
||||
) {
|
||||
val scrollToTop by feedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && feedContentState.scrollToTopPending) {
|
||||
listState.scrollToItem(index = 0)
|
||||
feedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
feedContentState: FeedContentState,
|
||||
listState: LazyGridState,
|
||||
) {
|
||||
val scrollToTop by feedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && feedContentState.scrollToTopPending) {
|
||||
listState.scrollToItem(index = 0)
|
||||
feedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchScrollToTop(
|
||||
videoFeedContentState: FeedContentState,
|
||||
pagerState: PagerState,
|
||||
) {
|
||||
val scrollToTop by videoFeedContentState.scrollToTop.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop > 0 && videoFeedContentState.scrollToTopPending) {
|
||||
pagerState.scrollToPage(page = 0)
|
||||
videoFeedContentState.sentToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.layouts
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ChatHeaderLayout(
|
||||
channelPicture: @Composable () -> Unit,
|
||||
firstRow: @Composable RowScope.() -> Unit,
|
||||
secondRow: @Composable RowScope.() -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(Size55Modifier) { channelPicture() }
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
firstRow()
|
||||
}
|
||||
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
secondRow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.layouts
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
|
||||
|
||||
@Composable
|
||||
fun LeftPictureLayout(
|
||||
onImage: @Composable () -> Unit,
|
||||
onTitleRow: @Composable RowScope.() -> Unit,
|
||||
onDescription: @Composable () -> Unit,
|
||||
onBottomRow: @Composable RowScope.() -> Unit,
|
||||
imageFraction: Float = 0.25f,
|
||||
) {
|
||||
Row(Modifier.aspectRatio(ratio = 1 / imageFraction)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(imageFraction).aspectRatio(ratio = 1f),
|
||||
) {
|
||||
onImage()
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
onTitleRow()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().weight(1f),
|
||||
) {
|
||||
onDescription()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
onBottomRow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-116
@@ -18,31 +18,22 @@
|
||||
* 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.layouts.listItem
|
||||
package com.vitorpamplona.amethyst.commons.ui.layouts.listItem
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemColors
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.FirstBaseline
|
||||
import androidx.compose.ui.layout.IntrinsicMeasurable
|
||||
import androidx.compose.ui.layout.IntrinsicMeasureScope
|
||||
@@ -55,117 +46,20 @@ import androidx.compose.ui.layout.MultiContentMeasurePolicy
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.offset
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_banner
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.ui.layouts.listItem.ListTokens.ListItemContainerElevation
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.NewItemsBubble
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.listItem.ListTokens.ListItemContainerElevation
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* This is a copy of Material3's ListItemLayout.kt file, with the only change being the padding change from 16.dp to 10.dp
|
||||
*/
|
||||
|
||||
private const val PREVIEW_AUTHOR = "This is my author"
|
||||
private const val PREVIEW_MESSAGE = "This is a message from this person"
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun ChannelNamePreview() {
|
||||
ThemeComparisonColumn {
|
||||
Column {
|
||||
ChatHeaderLayout(
|
||||
channelPicture = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 1),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
},
|
||||
firstRow = {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
},
|
||||
secondRow = {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
NewItemsBubble()
|
||||
},
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
SlimListItem(
|
||||
headlineContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
NewItemsBubble()
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 2),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Size55Modifier,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_AUTHOR, Modifier.weight(1f))
|
||||
TimeAgo(TimeUtils.now())
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Text(PREVIEW_MESSAGE, Modifier.weight(1f))
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
NewItemsBubble()
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
Image(
|
||||
painter = painterRes(R.drawable.github, 2),
|
||||
contentDescription = stringRes(id = Res.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Size55Modifier,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SlimListItem(
|
||||
headlineContent: @Composable () -> Unit,
|
||||
@@ -688,22 +582,22 @@ object ListTokens {
|
||||
|
||||
// Container related defaults
|
||||
// TODO: Make sure these values stay up to date until replaced with tokens.
|
||||
@VisibleForTesting internal val ListItemVerticalPadding = 8.dp
|
||||
internal val ListItemVerticalPadding = 8.dp
|
||||
|
||||
@VisibleForTesting internal val ListItemThreeLineVerticalPadding = 12.dp
|
||||
internal val ListItemThreeLineVerticalPadding = 12.dp
|
||||
|
||||
@VisibleForTesting internal val ListItemStartPadding = 10.dp
|
||||
internal val ListItemStartPadding = 10.dp
|
||||
|
||||
@VisibleForTesting internal val ListItemEndPadding = 10.dp
|
||||
internal val ListItemEndPadding = 10.dp
|
||||
|
||||
// Icon related defaults.
|
||||
// TODO: Make sure these values stay up to date until replaced with tokens.
|
||||
@VisibleForTesting internal val LeadingContentEndPadding = Modifier.padding(end = 10.dp)
|
||||
internal val LeadingContentEndPadding = Modifier.padding(end = 10.dp)
|
||||
|
||||
// Icon related defaults.
|
||||
// TODO: Make sure these values stay up to date until replaced with tokens.
|
||||
@VisibleForTesting internal val SupportingContentTopPadding = Modifier.padding(top = 2.dp)
|
||||
internal val SupportingContentTopPadding = Modifier.padding(top = 2.dp)
|
||||
|
||||
// Trailing related defaults.
|
||||
// TODO: Make sure these values stay up to date until replaced with tokens.
|
||||
@VisibleForTesting internal val TrailingContentStartPadding = Modifier.padding(start = 10.dp)
|
||||
internal val TrailingContentStartPadding = Modifier.padding(start = 10.dp)
|
||||
Reference in New Issue
Block a user