Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package me.rerere.rikkahub.ui.components.ui
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -39,14 +43,17 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import me.rerere.rikkahub.R
import me.rerere.rikkahub.ui.hooks.HapticPattern
import me.rerere.rikkahub.ui.hooks.rememberAvatarShape
import me.rerere.rikkahub.ui.hooks.rememberPremiumHaptics
import me.rerere.rikkahub.ui.theme.LocalDarkMode
import me.rerere.rikkahub.utils.ImageUtils

Expand Down Expand Up @@ -104,12 +111,33 @@ fun ClickableIconPicker(
modifier = modifier,
contentAlignment = Alignment.Center
) {
val haptics = rememberPremiumHaptics()
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

val scale by animateFloatAsState(
targetValue = if (isPressed) 0.85f else 1f,
animationSpec = spring(
dampingRatio = 0.6f,
stiffness = 300f
),
label = "icon_scale"
)

// Main icon area - clickable
Surface(
modifier = Modifier
.size(iconSize)
.graphicsLayer {
scaleX = scale
scaleY = scale
}
.clip(rememberAvatarShape(false))
.clickable {
.clickable(
interactionSource = interactionSource,
indication = null // Physics scale replaces ripple
) {
haptics.perform(HapticPattern.Pop)
if (hasCustomIcon) {
// Clear the custom icon
onIconCleared()
Expand Down Expand Up @@ -318,9 +346,34 @@ private fun LobeHubIconItem(
slug: String,
onSelect: () -> Unit
) {
val haptics = rememberPremiumHaptics()
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

val scale by animateFloatAsState(
targetValue = if (isPressed) 0.9f else 1f,
animationSpec = spring(
dampingRatio = 0.6f,
stiffness = 300f
),
label = "lobe_icon_scale"
)

Surface(
onClick = onSelect,
modifier = Modifier.size(56.dp),
modifier = Modifier
.size(56.dp)
.graphicsLayer {
scaleX = scale
scaleY = scale
}
.clip(RoundedCornerShape(12.dp))
.clickable(
interactionSource = interactionSource,
indication = null
) {
haptics.perform(HapticPattern.Pop)
onSelect()
},
shape = RoundedCornerShape(12.dp),
color = MaterialTheme.colorScheme.surfaceContainerHigh
) {
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/java/me/rerere/rikkahub/ui/components/ui/Select.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package me.rerere.rikkahub.ui.components.ui

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -24,6 +30,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import me.rerere.rikkahub.ui.hooks.HapticPattern
Expand Down Expand Up @@ -55,6 +63,18 @@ fun <T> Select(
label = "select_arrow_rotation"
)

// Physics: Press animation for the anchor
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val scale by animateFloatAsState(
targetValue = if (isPressed) 0.95f else 1f,
animationSpec = spring(
dampingRatio = 0.5f,
stiffness = 400f
),
label = "select_scale"
)

ExposedDropdownMenuBox(
modifier = modifier,
expanded = expanded,
Expand All @@ -67,6 +87,19 @@ fun <T> Select(
tonalElevation = 4.dp,
shape = AppShapes.ButtonPill,
modifier = Modifier
.pointerInput(Unit) {
awaitEachGesture {
val down = awaitFirstDown(requireUnconsumed = false)
val press = PressInteraction.Press(down.position)
interactionSource.emit(press)
waitForUpOrCancellation()
interactionSource.emit(PressInteraction.Release(press))
}
}
.graphicsLayer {
scaleX = scale
scaleY = scale
}
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
) {
Row(
Expand Down