mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-25 19:46:58 +02:00
Merge branch 'main' into refactor-media-compressor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@
|
|||||||
/.idea/ktfmt.xml
|
/.idea/ktfmt.xml
|
||||||
/.idea/studiobot.xml
|
/.idea/studiobot.xml
|
||||||
/.idea/other.xml
|
/.idea/other.xml
|
||||||
|
/.idea/runConfigurations.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
@@ -126,6 +126,7 @@ fun ZoomableContentView(
|
|||||||
|
|
||||||
val isLandscapeMode = DeviceUtils.isLandscapeMetric(LocalContext.current)
|
val isLandscapeMode = DeviceUtils.isLandscapeMetric(LocalContext.current)
|
||||||
val isFoldableOrLarge = DeviceUtils.windowIsLarge(windowSize = currentWindowSize, isInLandscapeMode = isLandscapeMode)
|
val isFoldableOrLarge = DeviceUtils.windowIsLarge(windowSize = currentWindowSize, isInLandscapeMode = isLandscapeMode)
|
||||||
|
val isOrientationLocked = DeviceUtils.screenOrientationIsLocked(LocalContext.current)
|
||||||
|
|
||||||
val contentScale =
|
val contentScale =
|
||||||
if (isFiniteHeight) {
|
if (isFiniteHeight) {
|
||||||
@@ -160,9 +161,9 @@ fun ZoomableContentView(
|
|||||||
nostrUriCallback = content.uri,
|
nostrUriCallback = content.uri,
|
||||||
onDialog = {
|
onDialog = {
|
||||||
dialogOpen = true
|
dialogOpen = true
|
||||||
// if (!isFoldableOrLarge) {
|
if (!isFoldableOrLarge && !isOrientationLocked) {
|
||||||
// DeviceUtils.changeDeviceOrientation(isLandscapeMode, activity)
|
DeviceUtils.changeDeviceOrientation(isLandscapeMode, activity)
|
||||||
// }
|
}
|
||||||
},
|
},
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
)
|
||||||
@@ -200,7 +201,7 @@ fun ZoomableContentView(
|
|||||||
images,
|
images,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
// if (!isFoldableOrLarge) DeviceUtils.changeDeviceOrientation(isLandscapeMode, activity)
|
if (!isFoldableOrLarge && !isOrientationLocked) DeviceUtils.changeDeviceOrientation(isLandscapeMode, activity)
|
||||||
},
|
},
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
)
|
)
|
||||||
|
@@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.components.util
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.provider.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.window.core.layout.WindowHeightSizeClass
|
import androidx.window.core.layout.WindowHeightSizeClass
|
||||||
@@ -38,6 +40,31 @@ object DeviceUtils {
|
|||||||
*/
|
*/
|
||||||
fun isLandscapeMetric(context: Context): Boolean = context.resources.displayMetrics.heightPixels < context.resources.displayMetrics.widthPixels
|
fun isLandscapeMetric(context: Context): Boolean = context.resources.displayMetrics.heightPixels < context.resources.displayMetrics.widthPixels
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the device's orientation is set to locked.
|
||||||
|
*
|
||||||
|
* Credits: NewPipe devs
|
||||||
|
*/
|
||||||
|
fun screenOrientationIsLocked(context: Context): Boolean {
|
||||||
|
// 1: Screen orientation changes using accelerometer
|
||||||
|
// 0: Screen orientation is locked
|
||||||
|
// if the accelerometer sensor is missing completely, assume locked orientation
|
||||||
|
return (
|
||||||
|
Settings.System.getInt(
|
||||||
|
context.contentResolver,
|
||||||
|
Settings.System.ACCELEROMETER_ROTATION,
|
||||||
|
0,
|
||||||
|
) == 0 ||
|
||||||
|
!context.packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the device's orientation. This works even if the device's orientation
|
||||||
|
* is set to locked.
|
||||||
|
* Thus, to prevent unwanted behaviour,
|
||||||
|
* it's use can be guarded by conditions such as [screenOrientationIsLocked].
|
||||||
|
*/
|
||||||
fun changeDeviceOrientation(
|
fun changeDeviceOrientation(
|
||||||
isInLandscape: Boolean,
|
isInLandscape: Boolean,
|
||||||
currentActivity: Activity,
|
currentActivity: Activity,
|
||||||
|
@@ -24,8 +24,10 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ContentTransform
|
import androidx.compose.animation.ContentTransform
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.animation.core.MutableTransitionState
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -89,6 +91,7 @@ import androidx.compose.ui.unit.TextUnit
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
|
import androidx.compose.ui.window.PopupProperties
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MediatorLiveData
|
import androidx.lifecycle.MediatorLiveData
|
||||||
@@ -146,6 +149,7 @@ import kotlinx.collections.immutable.persistentSetOf
|
|||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableSet
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -1325,23 +1329,52 @@ fun ReactionChoicePopup(
|
|||||||
.collectAsStateWithLifecycle()
|
.collectAsStateWithLifecycle()
|
||||||
val toRemove = remember { baseNote.reactedBy(accountViewModel.userProfile()).toImmutableSet() }
|
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(
|
Popup(
|
||||||
alignment = Alignment.BottomCenter,
|
alignment = Alignment.BottomCenter,
|
||||||
offset = IntOffset(0, iconSizePx),
|
offset = IntOffset(0, iconSizePx),
|
||||||
onDismissRequest = { onDismiss() },
|
onDismissRequest = { visibilityState.targetState = false },
|
||||||
|
properties = PopupProperties(focusable = true),
|
||||||
) {
|
) {
|
||||||
ReactionChoicePopupContent(
|
AnimatedVisibility(
|
||||||
reactions,
|
visibleState = visibilityState,
|
||||||
toRemove = toRemove,
|
enter =
|
||||||
onClick = { reactionType ->
|
slideInVertically(
|
||||||
accountViewModel.reactToOrDelete(
|
initialOffsetY = { it / 2 },
|
||||||
baseNote,
|
) + fadeIn(animationSpec = fadeAnimationSpec),
|
||||||
reactionType,
|
exit =
|
||||||
)
|
slideOutVertically(
|
||||||
onDismiss()
|
targetOffsetY = { it / 2 },
|
||||||
},
|
) + fadeOut(animationSpec = fadeAnimationSpec),
|
||||||
onChangeAmount,
|
) {
|
||||||
)
|
ReactionChoicePopupContent(
|
||||||
|
reactions,
|
||||||
|
toRemove = toRemove,
|
||||||
|
onClick = { reactionType ->
|
||||||
|
accountViewModel.reactToOrDelete(
|
||||||
|
baseNote,
|
||||||
|
reactionType,
|
||||||
|
)
|
||||||
|
visibilityState.targetState = false
|
||||||
|
},
|
||||||
|
onChangeAmount,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1503,58 +1536,87 @@ fun ZapAmountChoicePopup(
|
|||||||
|
|
||||||
val yOffset = with(LocalDensity.current) { -popupYOffset.toPx().toInt() }
|
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(
|
Popup(
|
||||||
alignment = Alignment.BottomCenter,
|
alignment = Alignment.BottomCenter,
|
||||||
offset = IntOffset(0, yOffset),
|
offset = IntOffset(0, yOffset),
|
||||||
onDismissRequest = { onDismiss() },
|
onDismissRequest = { visibilityState.targetState = false },
|
||||||
|
properties = PopupProperties(focusable = true),
|
||||||
) {
|
) {
|
||||||
FlowRow(horizontalArrangement = Arrangement.Center) {
|
AnimatedVisibility(
|
||||||
zapAmountChoices.forEach { amountInSats ->
|
visibleState = visibilityState,
|
||||||
Button(
|
enter =
|
||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
slideInVertically(
|
||||||
onClick = {
|
initialOffsetY = { it / 2 },
|
||||||
accountViewModel.zap(
|
) + fadeIn(animationSpec = fadeAnimationSpec),
|
||||||
baseNote,
|
exit =
|
||||||
amountInSats * 1000,
|
slideOutVertically(
|
||||||
null,
|
targetOffsetY = { it / 2 },
|
||||||
zapMessage,
|
) + fadeOut(animationSpec = fadeAnimationSpec),
|
||||||
context,
|
) {
|
||||||
true,
|
FlowRow(horizontalArrangement = Arrangement.Center) {
|
||||||
onError,
|
zapAmountChoices.forEach { amountInSats ->
|
||||||
onProgress,
|
Button(
|
||||||
onPayViaIntent,
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
)
|
onClick = {
|
||||||
onDismiss()
|
accountViewModel.zap(
|
||||||
},
|
baseNote,
|
||||||
shape = ButtonBorder,
|
amountInSats * 1000,
|
||||||
colors =
|
null,
|
||||||
ButtonDefaults.buttonColors(
|
zapMessage,
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
context,
|
||||||
),
|
true,
|
||||||
) {
|
onError,
|
||||||
Text(
|
onProgress,
|
||||||
"⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))}",
|
onPayViaIntent,
|
||||||
color = Color.White,
|
)
|
||||||
textAlign = TextAlign.Center,
|
visibilityState.targetState = false
|
||||||
modifier =
|
},
|
||||||
Modifier.combinedClickable(
|
shape = ButtonBorder,
|
||||||
onClick = {
|
colors =
|
||||||
accountViewModel.zap(
|
ButtonDefaults.buttonColors(
|
||||||
baseNote,
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
amountInSats * 1000,
|
|
||||||
null,
|
|
||||||
zapMessage,
|
|
||||||
context,
|
|
||||||
true,
|
|
||||||
onError,
|
|
||||||
onProgress,
|
|
||||||
onPayViaIntent,
|
|
||||||
)
|
|
||||||
onDismiss()
|
|
||||||
},
|
|
||||||
onLongClick = { onChangeAmount() },
|
|
||||||
),
|
),
|
||||||
)
|
) {
|
||||||
|
Text(
|
||||||
|
"⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))}",
|
||||||
|
color = Color.White,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier =
|
||||||
|
Modifier.combinedClickable(
|
||||||
|
onClick = {
|
||||||
|
accountViewModel.zap(
|
||||||
|
baseNote,
|
||||||
|
amountInSats * 1000,
|
||||||
|
null,
|
||||||
|
zapMessage,
|
||||||
|
context,
|
||||||
|
true,
|
||||||
|
onError,
|
||||||
|
onProgress,
|
||||||
|
onPayViaIntent,
|
||||||
|
)
|
||||||
|
visibilityState.targetState = false
|
||||||
|
},
|
||||||
|
onLongClick = { onChangeAmount() },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -109,7 +109,12 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
is Nip19Bech32.NEmbed -> null
|
is Nip19Bech32.NEmbed -> null
|
||||||
is Nip19Bech32.NRelay -> null
|
is Nip19Bech32.NRelay -> null
|
||||||
is Nip19Bech32.NAddress -> null
|
is Nip19Bech32.NAddress -> null
|
||||||
else -> null
|
else ->
|
||||||
|
try {
|
||||||
|
if (loginWithExternalSigner) Hex.decode(key) else null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginWithExternalSigner && pubKeyParsed == null) {
|
if (loginWithExternalSigner && pubKeyParsed == null) {
|
||||||
|
@@ -20,6 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -38,6 +44,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
@@ -76,46 +83,56 @@ fun ChannelFabColumn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
if (isOpen) {
|
AnimatedVisibility(
|
||||||
FloatingActionButton(
|
visible = isOpen,
|
||||||
onClick = {
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
wantsToSendNewMessage = true
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
isOpen = false
|
) {
|
||||||
},
|
Column {
|
||||||
modifier = Size55Modifier,
|
FloatingActionButton(
|
||||||
shape = CircleShape,
|
onClick = {
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
wantsToSendNewMessage = true
|
||||||
) {
|
isOpen = false
|
||||||
Text(
|
},
|
||||||
text = stringRes(R.string.messages_new_message),
|
modifier = Size55Modifier,
|
||||||
color = Color.White,
|
shape = CircleShape,
|
||||||
textAlign = TextAlign.Center,
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
fontSize = Font12SP,
|
) {
|
||||||
)
|
Text(
|
||||||
|
text = stringRes(R.string.messages_new_message),
|
||||||
|
color = Color.White,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = Font12SP,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToCreateChannel = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringRes(R.string.messages_create_public_chat),
|
||||||
|
color = Color.White,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = Font12SP,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
|
|
||||||
FloatingActionButton(
|
|
||||||
onClick = {
|
|
||||||
wantsToCreateChannel = true
|
|
||||||
isOpen = false
|
|
||||||
},
|
|
||||||
modifier = Size55Modifier,
|
|
||||||
shape = CircleShape,
|
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringRes(R.string.messages_create_public_chat),
|
|
||||||
color = Color.White,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
fontSize = Font12SP,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val rotationDegree by animateFloatAsState(
|
||||||
|
targetValue = if (isOpen) 45f else 0f,
|
||||||
|
)
|
||||||
|
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = { isOpen = !isOpen },
|
onClick = { isOpen = !isOpen },
|
||||||
modifier = Size55Modifier,
|
modifier = Size55Modifier,
|
||||||
@@ -125,7 +142,10 @@ fun ChannelFabColumn(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Add,
|
imageVector = Icons.Outlined.Add,
|
||||||
contentDescription = stringRes(R.string.messages_create_public_private_chat_description),
|
contentDescription = stringRes(R.string.messages_create_public_private_chat_description),
|
||||||
modifier = Modifier.size(26.dp),
|
modifier =
|
||||||
|
Modifier.size(26.dp).graphicsLayer {
|
||||||
|
rotationZ = rotationDegree
|
||||||
|
},
|
||||||
tint = Color.White,
|
tint = Color.White,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.expandVertically
|
||||||
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -73,7 +78,11 @@ fun SummaryBar(state: NotificationSummaryState) {
|
|||||||
|
|
||||||
UserReactionsRow(state) { showChart = !showChart }
|
UserReactionsRow(state) { showChart = !showChart }
|
||||||
|
|
||||||
if (showChart) {
|
AnimatedVisibility(
|
||||||
|
visible = showChart,
|
||||||
|
enter = slideInVertically() + expandVertically(),
|
||||||
|
exit = slideOutVertically() + shrinkVertically(),
|
||||||
|
) {
|
||||||
val lineChartCount =
|
val lineChartCount =
|
||||||
lineChart(
|
lineChart(
|
||||||
lines =
|
lines =
|
||||||
|
@@ -25,7 +25,12 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -36,6 +41,7 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AddPhotoAlternate
|
import androidx.compose.material.icons.filled.AddPhotoAlternate
|
||||||
import androidx.compose.material.icons.filled.CameraAlt
|
import androidx.compose.material.icons.filled.CameraAlt
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -186,44 +192,51 @@ fun NewImageButton(
|
|||||||
ShowProgress(postViewModel)
|
ShowProgress(postViewModel)
|
||||||
} else {
|
} else {
|
||||||
Column {
|
Column {
|
||||||
if (isOpen) {
|
// if (isOpen) {
|
||||||
FloatingActionButton(
|
AnimatedVisibility(
|
||||||
onClick = {
|
visible = isOpen,
|
||||||
wantsToPostFromCamera = true
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
isOpen = false
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
},
|
) {
|
||||||
modifier = Size55Modifier,
|
Column {
|
||||||
shape = CircleShape,
|
FloatingActionButton(
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
onClick = {
|
||||||
) {
|
wantsToPostFromCamera = true
|
||||||
Icon(
|
isOpen = false
|
||||||
imageVector = Icons.Default.CameraAlt,
|
},
|
||||||
contentDescription = stringRes(id = R.string.upload_image),
|
modifier = Size55Modifier,
|
||||||
modifier = Modifier.size(26.dp),
|
shape = CircleShape,
|
||||||
tint = Color.White,
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
)
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.CameraAlt,
|
||||||
|
contentDescription = stringRes(id = R.string.upload_image),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToPostFromGallery = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.AddPhotoAlternate,
|
||||||
|
contentDescription = stringRes(id = R.string.upload_image),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
|
|
||||||
FloatingActionButton(
|
|
||||||
onClick = {
|
|
||||||
wantsToPostFromGallery = true
|
|
||||||
isOpen = false
|
|
||||||
},
|
|
||||||
modifier = Size55Modifier,
|
|
||||||
shape = CircleShape,
|
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.AddPhotoAlternate,
|
|
||||||
contentDescription = stringRes(id = R.string.upload_image),
|
|
||||||
modifier = Modifier.size(26.dp),
|
|
||||||
tint = Color.White,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
@@ -234,12 +247,31 @@ fun NewImageButton(
|
|||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
) {
|
||||||
Icon(
|
AnimatedVisibility(
|
||||||
painter = painterResource(R.drawable.ic_compose),
|
visible = isOpen,
|
||||||
contentDescription = stringRes(id = R.string.new_short),
|
enter = fadeIn(),
|
||||||
modifier = Modifier.size(26.dp),
|
exit = fadeOut(),
|
||||||
tint = Color.White,
|
) {
|
||||||
)
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringRes(id = R.string.new_short),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = !isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_compose),
|
||||||
|
contentDescription = stringRes(id = R.string.new_short),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanistAdaptive = "0.34.0"
|
accompanistAdaptive = "0.34.0"
|
||||||
activityCompose = "1.9.2"
|
activityCompose = "1.9.2"
|
||||||
agp = "8.6.1"
|
agp = "8.7.0"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "34"
|
||||||
android-minSdk = "26"
|
android-minSdk = "26"
|
||||||
android-targetSdk = "34"
|
android-targetSdk = "34"
|
||||||
@@ -9,16 +9,16 @@ androidKotlinGeohash = "1.0"
|
|||||||
androidxJunit = "1.2.1"
|
androidxJunit = "1.2.1"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.0"
|
||||||
audiowaveform = "1.1.1"
|
audiowaveform = "1.1.1"
|
||||||
benchmark = "1.3.1"
|
benchmark = "1.3.2"
|
||||||
benchmarkJunit4 = "1.3.1"
|
benchmarkJunit4 = "1.3.2"
|
||||||
biometricKtx = "1.2.0-alpha05"
|
biometricKtx = "1.2.0-alpha05"
|
||||||
blurhash = "1.0.0"
|
blurhash = "1.0.0"
|
||||||
coil = "2.7.0"
|
coil = "2.7.0"
|
||||||
composeBom = "2024.09.02"
|
composeBom = "2024.09.03"
|
||||||
coreKtx = "1.13.1"
|
coreKtx = "1.13.1"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
firebaseBom = "33.3.0"
|
firebaseBom = "33.4.0"
|
||||||
fragmentKtx = "1.8.3"
|
fragmentKtx = "1.8.4"
|
||||||
gms = "4.4.2"
|
gms = "4.4.2"
|
||||||
jacksonModuleKotlin = "2.17.2"
|
jacksonModuleKotlin = "2.17.2"
|
||||||
jna = "5.14.0"
|
jna = "5.14.0"
|
||||||
@@ -36,7 +36,7 @@ markdown = "077a2cde64"
|
|||||||
media3 = "1.4.1"
|
media3 = "1.4.1"
|
||||||
mockk = "1.13.12"
|
mockk = "1.13.12"
|
||||||
kotlinx-coroutines-test = "1.9.0-RC.2"
|
kotlinx-coroutines-test = "1.9.0-RC.2"
|
||||||
navigationCompose = "2.8.1"
|
navigationCompose = "2.8.2"
|
||||||
okhttp = "5.0.0-alpha.14"
|
okhttp = "5.0.0-alpha.14"
|
||||||
runner = "1.6.2"
|
runner = "1.6.2"
|
||||||
rfc3986 = "0.1.0"
|
rfc3986 = "0.1.0"
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Wed Jan 04 09:23:50 EST 2023
|
#Wed Jan 04 09:23:50 EST 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
Reference in New Issue
Block a user