Merge pull request #786 from permissionlesstech/ui/noise-open-lock-transition

ui: open lock until Noise session is established
This commit is contained in:
callebtc 2026-07-27 18:46:15 +02:00 committed by GitHub
commit f3c4571cc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4,6 +4,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
import androidx.compose.animation.Crossfade
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.RepeatMode
@ -294,10 +295,10 @@ internal fun TorAwareHeaderIcon(
/** /**
* Noise session status for private-chat headers. * Noise session status for private-chat headers.
* *
* Same visual language as the main header's Tor-aware globe: one lock glyph throughout, tint * Same visual language as the main header's Tor-aware globe: tint cross-fades between states,
* cross-fades between states, and a soft radial glow pulse while the handshake is in flight. * and a soft radial glow pulse while the handshake is in flight. The glyph itself is the open
* The old sync/recycle glyph is gone progress is carried by colour and motion, not by swapping * lock until a session is established (or fails), then the closed lock both share the same
* icons. * baseline so a [Crossfade] reads as the shackle settling shut rather than an icon swap.
*/ */
@Composable @Composable
fun NoiseSessionIcon( fun NoiseSessionIcon(
@ -324,28 +325,42 @@ fun NoiseSessionIcon(
stringResource(R.string.cd_handshake_failed) stringResource(R.string.cd_handshake_failed)
) )
else -> Triple( else -> Triple(
// Not yet started — quiet grey lock, same glyph as every other state. // Not yet started — quiet grey open lock.
colorScheme.onSurfaceVariant, colorScheme.onSurfaceVariant,
false, false,
stringResource(R.string.cd_ready_for_handshake) stringResource(R.string.cd_ready_for_handshake)
) )
} }
// Longer than the usual chrome tint so grey → orange → green reads as a continuous wash, // Closed once the handshake resolves (success or failure); open while idle or in flight.
// not a snap between discrete states. val lockIconRes = when {
sessionState == "established" || sessionState?.startsWith("failed") == true ->
R.drawable.ic_spec_lock
else -> R.drawable.ic_spec_lock_open
}
// Match the tint wash so open → closed and grey → orange → green land together.
val lockTransitionMs = 480
val animatedTint by animateColorAsState( val animatedTint by animateColorAsState(
targetValue = targetTint, targetValue = targetTint,
animationSpec = tween(durationMillis = 480, easing = FastOutSlowInEasing), animationSpec = tween(durationMillis = lockTransitionMs, easing = FastOutSlowInEasing),
label = "noiseSessionTint" label = "noiseSessionTint"
) )
TorAwareHeaderIcon( Crossfade(
painter = painterResource(R.drawable.ic_spec_lock), targetState = lockIconRes,
tint = animatedTint, animationSpec = tween(durationMillis = lockTransitionMs, easing = FastOutSlowInEasing),
isProgress = isProgress, modifier = modifier,
contentDescription = contentDescription, label = "noiseLockGlyph"
modifier = modifier ) { iconRes ->
) TorAwareHeaderIcon(
painter = painterResource(iconRes),
tint = animatedTint,
isProgress = isProgress,
contentDescription = contentDescription,
)
}
} }
/** /**