Compare commits

..
11 Commits
16 changed files with 217 additions and 254 deletions
+4
View File
@@ -37,6 +37,10 @@
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
+2 -2
View File
@@ -16,8 +16,8 @@ android {
applicationId "com.vitorpamplona.amethyst"
minSdk libs.versions.android.minSdk.get().toInteger()
targetSdk libs.versions.android.targetSdk.get().toInteger()
versionCode 407
versionName "0.92.2"
versionCode 408
versionName "0.92.3"
buildConfigField "String", "RELEASE_NOTES_ID", "\"98f2acaf9b3eb70d87607942e576f9a086ac28adf37869da9a29f4815cecac86\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -26,6 +26,8 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloatAsState
@@ -110,6 +112,7 @@ import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.note.types.EditState
import com.vitorpamplona.amethyst.ui.note.types.RenderReaction
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
@@ -135,6 +138,9 @@ import com.vitorpamplona.amethyst.ui.theme.Size28Modifier
import com.vitorpamplona.amethyst.ui.theme.SmallBorder
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.ui.theme.TinyBorders
import com.vitorpamplona.amethyst.ui.theme.defaultTweenDuration
import com.vitorpamplona.amethyst.ui.theme.defaultTweenFloatSpec
import com.vitorpamplona.amethyst.ui.theme.defaultTweenIntOffsetSpec
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.reactionBox
@@ -1014,20 +1020,26 @@ fun ZapReaction(
interactionSource = remember { MutableInteractionSource() },
indication = ripple24dp,
onClick = {
zapClick(
baseNote,
accountViewModel,
context,
onZappingProgress = { progress: Float -> scope.launch { zappingProgress = progress } },
onMultipleChoices = { wantsToZap = true },
onError = { _, message ->
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = showErrorMessageDialog + message
}
},
onPayViaIntent = { wantsToPay = it },
)
scope.launch {
zapClick(
baseNote,
accountViewModel,
context,
onZappingProgress = { progress: Float -> scope.launch { zappingProgress = progress } },
onMultipleChoices = {
scope.launch {
wantsToZap = true
}
},
onError = { _, message ->
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = showErrorMessageDialog + message
}
},
onPayViaIntent = { wantsToPay = it },
)
}
},
onLongClick = { wantsToChangeZapAmount = true },
onDoubleClick = { wantsToSetCustomZap = true },
@@ -1043,8 +1055,10 @@ fun ZapReaction(
zappingProgress = 0f
},
onChangeAmount = {
wantsToZap = false
wantsToChangeZapAmount = true
scope.launch {
wantsToZap = false
wantsToChangeZapAmount = true
}
},
onError = { _, message ->
scope.launch {
@@ -1249,7 +1263,6 @@ fun ObserveZapAmountText(
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun BoostTypeChoicePopup(
baseNote: Note,
@@ -1259,62 +1272,120 @@ private fun BoostTypeChoicePopup(
onQuote: () -> Unit,
onRepost: () -> Unit,
onFork: () -> Unit,
) {
val visibilityState = rememberVisibilityState(onDismiss)
BoostTypeChoicePopup(
baseNote,
iconSize,
accountViewModel,
visibilityState,
onQuote,
onRepost,
onFork,
)
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun BoostTypeChoicePopup(
baseNote: Note,
iconSize: Dp,
accountViewModel: AccountViewModel,
visibilityState: MutableTransitionState<Boolean>,
onQuote: () -> Unit,
onRepost: () -> Unit,
onFork: () -> Unit,
) {
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
Popup(
alignment = Alignment.BottomCenter,
offset = IntOffset(0, iconSizePx),
onDismissRequest = { onDismiss() },
onDismissRequest = { visibilityState.targetState = false },
) {
FlowRow {
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
if (accountViewModel.isWriteable()) {
accountViewModel.boost(baseNote)
onDismiss()
} else {
onRepost()
onDismiss()
}
},
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.boost), color = Color.White, textAlign = TextAlign.Center)
}
AnimatedVisibility(
visibleState = visibilityState,
enter = popupAnimationEnter,
exit = popupAnimationExit,
) {
FlowRow {
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
if (accountViewModel.isWriteable()) {
accountViewModel.boost(baseNote)
visibilityState.targetState = false
} else {
onRepost()
visibilityState.targetState = false
}
},
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.boost), color = Color.White, textAlign = TextAlign.Center)
}
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onQuote,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
}
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onQuote,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
}
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onFork,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onFork,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
}
}
}
}
}
val popupAnimationEnter: EnterTransition =
slideInVertically(
animationSpec = defaultTweenIntOffsetSpec,
initialOffsetY = { it / 2 },
) + fadeIn(animationSpec = defaultTweenFloatSpec)
val popupAnimationExit: ExitTransition =
slideOutVertically(
animationSpec = defaultTweenIntOffsetSpec,
targetOffsetY = { it / 2 },
) + fadeOut(animationSpec = defaultTweenFloatSpec)
@Composable
fun rememberVisibilityState(onDismiss: () -> Unit): MutableTransitionState<Boolean> {
// Prevent multiple calls to onDismiss()
var dismissed by remember { mutableStateOf(false) }
val visibilityState = remember { MutableTransitionState(false).apply { targetState = true } }
LaunchedEffect(visibilityState.targetState) {
if (!visibilityState.targetState && !dismissed) {
delay(defaultTweenDuration.toLong())
dismissed = true
onDismiss()
}
}
return visibilityState
}
@Composable
fun ReactionChoicePopup(
baseNote: Note,
@@ -1322,6 +1393,18 @@ fun ReactionChoicePopup(
accountViewModel: AccountViewModel,
onDismiss: () -> Unit,
onChangeAmount: () -> Unit,
) {
val visibilityState = rememberVisibilityState(onDismiss)
ReactionChoicePopup(baseNote, iconSize, accountViewModel, visibilityState, onChangeAmount)
}
@Composable
fun ReactionChoicePopup(
baseNote: Note,
iconSize: Dp,
accountViewModel: AccountViewModel,
visibilityState: MutableTransitionState<Boolean>,
onChangeAmount: () -> Unit,
) {
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
@@ -1329,22 +1412,6 @@ fun ReactionChoicePopup(
.collectAsStateWithLifecycle()
val toRemove = remember { baseNote.reactedBy(accountViewModel.userProfile()).toImmutableSet() }
// Define animation specs
val animationDuration = 250
val fadeAnimationSpec = tween<Float>(durationMillis = animationDuration)
// Prevent multiple calls to onDismiss()
var dismissed by remember { mutableStateOf(false) }
val visibilityState = remember { MutableTransitionState(false).apply { targetState = true } }
LaunchedEffect(visibilityState.targetState) {
if (!visibilityState.targetState && !dismissed) {
delay(animationDuration.toLong())
dismissed = true
onDismiss()
}
}
Popup(
alignment = Alignment.BottomCenter,
offset = IntOffset(0, iconSizePx),
@@ -1353,14 +1420,8 @@ fun ReactionChoicePopup(
) {
AnimatedVisibility(
visibleState = visibilityState,
enter =
slideInVertically(
initialOffsetY = { it / 2 },
) + fadeIn(animationSpec = fadeAnimationSpec),
exit =
slideOutVertically(
targetOffsetY = { it / 2 },
) + fadeOut(animationSpec = fadeAnimationSpec),
enter = popupAnimationEnter,
exit = popupAnimationExit,
) {
ReactionChoicePopupContent(
reactions,
@@ -1518,7 +1579,6 @@ fun ZapAmountChoicePopup(
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, onDismiss, onChangeAmount, onError, onProgress, onPayViaIntent)
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
fun ZapAmountChoicePopup(
baseNote: Note,
@@ -1530,28 +1590,29 @@ fun ZapAmountChoicePopup(
onError: (title: String, text: String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
) {
val visibilityState = rememberVisibilityState(onDismiss)
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, visibilityState, onChangeAmount, onError, onProgress, onPayViaIntent)
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
fun ZapAmountChoicePopup(
baseNote: Note,
zapAmountChoices: ImmutableList<Long>,
accountViewModel: AccountViewModel,
popupYOffset: Dp,
visibilityState: MutableTransitionState<Boolean>,
onChangeAmount: () -> Unit,
onError: (title: String, text: String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
) {
val context = LocalContext.current
val zapMessage = ""
val yOffset = with(LocalDensity.current) { -popupYOffset.toPx().toInt() }
// Define animation specs
val animationDuration = 250
val fadeAnimationSpec = tween<Float>(durationMillis = animationDuration)
// Prevent multiple calls to onDismiss()
var dismissed by remember { mutableStateOf(false) }
val visibilityState = remember { MutableTransitionState(false).apply { targetState = true } }
LaunchedEffect(visibilityState.targetState) {
if (!visibilityState.targetState && !dismissed) {
delay(animationDuration.toLong())
dismissed = true
onDismiss()
}
}
Popup(
alignment = Alignment.BottomCenter,
offset = IntOffset(0, yOffset),
@@ -1560,14 +1621,8 @@ fun ZapAmountChoicePopup(
) {
AnimatedVisibility(
visibleState = visibilityState,
enter =
slideInVertically(
initialOffsetY = { it / 2 },
) + fadeIn(animationSpec = fadeAnimationSpec),
exit =
slideOutVertically(
targetOffsetY = { it / 2 },
) + fadeOut(animationSpec = fadeAnimationSpec),
enter = popupAnimationEnter,
exit = popupAnimationExit,
) {
FlowRow(horizontalArrangement = Arrangement.Center) {
zapAmountChoices.forEach { amountInSats ->
@@ -1267,7 +1267,7 @@ class AccountViewModel(
LocalCache.live.newEventBundles.collect { newNotes ->
Log.d(
"Rendering Metrics",
"Update feeds ${this@AccountViewModel} for ${account.userProfile().toBestDisplayName()}",
"Update feeds ${this@AccountViewModel} for ${account.userProfile().toBestDisplayName()} with ${newNotes.size} new notes",
)
feedStates.updateFeedsWith(newNotes)
upgradeAttestations()
@@ -26,6 +26,7 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.focus.FocusRequester
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.vitorpamplona.amethyst.model.Account
@@ -43,6 +44,7 @@ import kotlinx.coroutines.flow.asStateFlow
class SearchBarViewModel(
val account: Account,
) : ViewModel() {
val focusRequester = FocusRequester()
var searchValue by mutableStateOf("")
private var _searchResultsUsers = MutableStateFlow<List<User>>(emptyList())
@@ -52,7 +52,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLifecycleOwner
@@ -91,7 +90,6 @@ import com.vitorpamplona.amethyst.ui.theme.StdTopPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -148,6 +146,10 @@ fun SearchScreen(
val listState = rememberLazyListState()
LaunchedEffect(searchBarViewModel.focusRequester) {
searchBarViewModel.focusRequester.requestFocus()
}
DisappearingScaffold(
isInvertedLayout = false,
topBar = {
@@ -234,15 +236,6 @@ private fun SearchTextField(
modifier: Modifier,
onTextChanges: (String) -> Unit,
) {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
launch {
delay(100)
focusRequester.requestFocus()
}
}
Row(
modifier = modifier.padding(10.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
@@ -264,7 +257,7 @@ private fun SearchTextField(
Modifier
.weight(1f, true)
.defaultMinSize(minHeight = 20.dp)
.focusRequester(focusRequester),
.focusRequester(searchBarViewModel.focusRequester),
placeholder = {
Text(
text = stringRes(R.string.npub_hex_username),
@@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -196,9 +197,9 @@ import com.vitorpamplona.quartz.events.TorrentCommentEvent
import com.vitorpamplona.quartz.events.TorrentEvent
import com.vitorpamplona.quartz.events.VideoEvent
import com.vitorpamplona.quartz.events.WikiNoteEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@@ -235,26 +236,32 @@ fun RenderThreadFeed(
nav: INav,
) {
val items by loaded.feed.collectAsStateWithLifecycle()
val firstTimeScrolled = remember { TimeUtils.now() }
LaunchedEffect(noteId) {
// waits to load the thread to scroll to item.
delay(100)
val noteForPosition =
items.list
.filter { it.idHex == noteId }
.firstOrNull()
var position = items.list.indexOf(noteForPosition)
if (position >= 0) {
if (position >= 1 && position < items.list.size - 1) {
position-- // show the replying note
LaunchedEffect(noteId, items.list) {
// hack to allow multiple scrolls to Item while posts on the screen load.
// This is important when clicking on a reply of an older thread in Notifications
// In that case, this screen will open with 0-1 items, and the scrollToItem below
// will not change the state of the screen (too few items, scroll is not available)
// as the app loads the reaming of the thread the position of the reply changes
// and becuase there wasn't a possibility to scroll before and now there is one,
// the screen stays at the top. Once the thread has enough replies, the lazy column
// updates with new items correctly. It just needs a few items to start the scrool.
//
// This hack allows the list 1 second to fill up with more
// records before setting up the position on the feed.
//
// It jumps around, but it is the best we can do.
if (TimeUtils.now() - firstTimeScrolled < 1000) {
val position = items.list.indexOfFirst { it.idHex == noteId }
if (position >= 0) {
listState.scrollToItem(position, -200)
}
listState.scrollToItem(position)
}
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = FeedPadding,
state = listState,
) {
@@ -1,111 +0,0 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.video
import androidx.annotation.FloatRange
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.foundation.pager.PagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
import kotlin.math.abs
/**
* This file only exists to fix an interference between the Disappearing Top
* and Bottom Scaffold bars and the offsetFraction of the pager. The current
* implementation ends the scroll at a state where some fraction is still present
* which places videos away from their natural position in the page.
*
* The fix simply animates it back to the fraction = 0.
*/
@Composable
fun myRememberPagerState(
initialPage: Int = 0,
@FloatRange(from = -0.5, to = 0.5) initialPageOffsetFraction: Float = 0f,
pageCount: () -> Int,
): PagerState =
rememberSaveable(saver = DefaultPagerState.Saver) {
DefaultPagerState(
initialPage,
initialPageOffsetFraction,
pageCount,
)
}.apply {
pageCountState.value = pageCount
}
@Composable
fun myRememberForeverPagerState(
key: String,
initialFirstVisibleItemIndex: Int = 0,
initialFirstVisibleItemScrollOffset: Float = 0.0f,
pageCount: () -> Int,
): PagerState =
rememberForeverPagerState(key, initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset, pageCount) { initialPage, initialPageOffsetFraction, pageCount ->
myRememberPagerState(initialPage, initialPageOffsetFraction, pageCount)
}
private class DefaultPagerState(
currentPage: Int,
currentPageOffsetFraction: Float,
updatedPageCount: () -> Int,
) : PagerState(currentPage, currentPageOffsetFraction) {
var pageCountState = mutableStateOf(updatedPageCount)
override val pageCount: Int get() = pageCountState.value.invoke()
override suspend fun scroll(
scrollPriority: MutatePriority,
block: suspend ScrollScope.() -> Unit,
) {
super.scroll(scrollPriority, block)
if (abs(currentPageOffsetFraction) > 0) {
animateScrollToPage(currentPage, 0f)
}
}
companion object {
/**
* To keep current page and current page offset saved
*/
val Saver: Saver<DefaultPagerState, *> =
listSaver(
save = {
listOf(
it.currentPage,
(it.currentPageOffsetFraction).coerceIn(-0.5f, 0.5f),
it.pageCount,
)
},
restore = {
DefaultPagerState(
currentPage = it[0] as Int,
currentPageOffsetFraction = it[1] as Float,
updatedPageCount = { it[2] as Int },
)
},
)
}
}
@@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Icon
@@ -67,6 +68,7 @@ import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
import com.vitorpamplona.amethyst.ui.navigation.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.Route
@@ -245,9 +247,9 @@ fun SlidingCarousel(
val pagerState =
if (pagerStateKey != null) {
myRememberForeverPagerState(pagerStateKey, items.list.size) { items.list.size }
rememberForeverPagerState(pagerStateKey, items.list.size) { items.list.size }
} else {
myRememberPagerState(items.list.size) { items.list.size }
rememberPagerState(items.list.size) { items.list.size }
}
WatchScrollToTop(videoFeedContentState, pagerState)
@@ -20,6 +20,7 @@
*/
package com.vitorpamplona.amethyst.ui.theme
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
@@ -40,6 +41,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
val Shapes =
@@ -287,3 +289,7 @@ val reactionBox =
.padding(5.dp)
val ripple24dp = ripple(bounded = false, radius = Size24dp)
val defaultTweenDuration = 100
val defaultTweenFloatSpec = tween<Float>(durationMillis = defaultTweenDuration)
val defaultTweenIntOffsetSpec = tween<IntOffset>(durationMillis = defaultTweenDuration)
@@ -520,6 +520,7 @@
<string name="light">Clair</string>
<string name="dark">Sombre</string>
<string name="application_preferences">Préférences de l\'application</string>
<string name="wallet_connect">Wallet Connect</string>
<string name="language">Langage</string>
<string name="theme">Thème</string>
<string name="automatically_load_images_gifs">Prévisualisation des images</string>
@@ -520,6 +520,7 @@
<string name="light">प्रकाशवान</string>
<string name="dark">अन्धकारमय</string>
<string name="application_preferences">क्रमक आद्यताएँ</string>
<string name="wallet_connect">धनकोष संयोजन</string>
<string name="language">भाषा</string>
<string name="theme">प्रदर्शनशैली</string>
<string name="automatically_load_images_gifs">चित्र पूर्वीक्षण</string>
@@ -520,6 +520,7 @@
<string name="light">Licht</string>
<string name="dark">Donker</string>
<string name="application_preferences">Application preferences</string>
<string name="wallet_connect">Wallet Connect</string>
<string name="language">Taal</string>
<string name="theme">Thema</string>
<string name="automatically_load_images_gifs">Automatisch afbeeldingen/gifs laden</string>
@@ -520,6 +520,7 @@
<string name="light">Jasny</string>
<string name="dark">Ciemny</string>
<string name="application_preferences">Ustawienia Aplikacji</string>
<string name="wallet_connect">Podłącz portfel</string>
<string name="language">Język</string>
<string name="theme">Motyw</string>
<string name="automatically_load_images_gifs">Podgląd obrazu</string>
@@ -9,6 +9,7 @@ Prijavi se s privatnim ključem za všečkanje sporočila</string>
<string name="public_chat_feed">Javni vir novic</string>
<string name="add_a_relay">Dodaj rele</string>
<string name="display_name">Prikazno ime</string>
<string name="uploading">Nalaganje …</string>
<string name="npub_hex_username">"npub, uporabniško ime, tekst"</string>
<string name="quick_action_share_browser_link">Deli povezavo brskalnika</string>
<string name="quick_action_request_deletion_gallery_title">Izbris iz galerije</string>
+3 -3
View File
@@ -1,10 +1,10 @@
[versions]
accompanistAdaptive = "0.34.0"
activityCompose = "1.9.2"
agp = "8.7.0"
android-compileSdk = "34"
agp = "8.7.1"
android-compileSdk = "35"
android-minSdk = "26"
android-targetSdk = "34"
android-targetSdk = "35"
androidKotlinGeohash = "1.0"
androidxJunit = "1.2.1"
appcompat = "1.7.0"