Skip to content

Commit

Permalink
Shared, Android: New first session onboarding (#1080)
Browse files Browse the repository at this point in the history
^ALTAPPS-1277
  • Loading branch information
XanderZhu authored Jun 19, 2024
1 parent a51bf61 commit 8d5653d
Show file tree
Hide file tree
Showing 199 changed files with 5,659 additions and 839 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.Preview
import org.hyperskill.app.R
import org.hyperskill.app.android.core.view.ui.widget.compose.HtmlText
import org.hyperskill.app.android.core.view.ui.widget.compose.ClickableHtmlText
import org.hyperskill.app.android.core.view.ui.widget.compose.HyperskillTheme

@Composable
Expand All @@ -16,7 +16,7 @@ fun ChallengeDescription(
onLinkClick: (String) -> Unit,
modifier: Modifier = Modifier
) {
HtmlText(
ClickableHtmlText(
text = description,
modifier = modifier,
baseSpanStyle = SpanStyle(color = colorResource(id = R.color.color_on_surface_alpha_60)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ private const val URL_TAG = "url"

@Composable
fun HtmlText(
text: String,
baseSpanStyle: SpanStyle? = null,
content: @Composable (AnnotatedString) -> Unit
) {
val annotatedString = remember(text) {
HtmlCompat
.fromHtml(
text.replace("\n", "<br>"),
HtmlCompat.FROM_HTML_MODE_COMPACT
)
.toAnnotatedString(
baseSpanStyle = baseSpanStyle,
underlineLinks = false
)
}
content(annotatedString)
}

@Composable
fun ClickableHtmlText(
text: String,
modifier: Modifier = Modifier,
baseSpanStyle: SpanStyle? = null,
Expand All @@ -35,15 +55,19 @@ fun HtmlText(
style: TextStyle = LocalTextStyle.current,
onUrlClick: ((url: String) -> Unit)? = null
) {
val spannedText = remember(text) {
HtmlCompat.fromHtml(text.replace("\n", "<br>"), HtmlCompat.FROM_HTML_MODE_COMPACT)
val annotatedString = remember(text) {
HtmlCompat
.fromHtml(
text.replace("\n", "<br>"),
HtmlCompat.FROM_HTML_MODE_COMPACT
)
.toAnnotatedString(
baseSpanStyle = baseSpanStyle,
linkColor = if (isHighlightLink) linkColor else Color.Unspecified,
underlineLinks = false
)
}
val uriHandler = LocalUriHandler.current
val annotatedString = spannedText.toAnnotatedString(
baseSpanStyle = baseSpanStyle,
linkColor = if (isHighlightLink) linkColor else Color.Unspecified,
underlineLinks = false
)
ClickableText(
modifier = modifier,
text = annotatedString,
Expand All @@ -62,7 +86,7 @@ fun HtmlText(
fun Spanned.toAnnotatedString(
baseSpanStyle: SpanStyle?,
underlineLinks: Boolean,
linkColor: Color
linkColor: Color = Color.Blue
): AnnotatedString =
buildAnnotatedString {
val spanned = this@toAnnotatedString
Expand Down Expand Up @@ -101,7 +125,7 @@ fun Spanned.toAnnotatedString(
@Preview
@Composable
private fun LinksHtmlTextPreview() {
HtmlText(
ClickableHtmlText(
/*ktlint-disable*/
text = "<b>Some text</b> \n<a href=\"https://developer.android.com/jetpack/androidx/releases/compose\" target=\"_blank\">" +
"link text</a>, the rest of the text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ object HyperskillButtonDefaults {
): ButtonColors =
ButtonDefaults.buttonColors(
backgroundColor = backgroundColor,
disabledBackgroundColor = backgroundColor.copy(alpha = 0.38f),
contentColor = contentColor
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import org.hyperskill.app.android.R

@Composable
fun HyperskillCard(
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
cornerRadius: Dp = dimensionResource(id = R.dimen.corner_radius),
contentPadding: PaddingValues? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onClick: (() -> Unit)? = null,
content: @Composable BoxScope.() -> Unit
Expand All @@ -42,7 +42,11 @@ fun HyperskillCard(
it
}
}
.padding(contentPadding),
.apply {
if (contentPadding != null) {
padding(contentPadding)
}
},
content = content
)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.hyperskill.app.android.core.view.ui.widget.compose

import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.DurationBasedAnimationSpec
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
Expand All @@ -16,16 +20,25 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color

private const val INITIAL_VALUE = -2f
private const val TARGET_VALUE = 2f

private val DefaultColors: List<Color> = listOf(
Color.Transparent,
Color.White.copy(alpha = 0.7f),
Color.Transparent
)

/**
* Applies shimmer animation to the target Composable.
* Animation is playing one time.
* To start animation call [ShimmerState.runShimmerAnimation] on the [ShimmerState] instance.
* To start animation call [ShimmerShotState.runShimmerAnimation] on the [ShimmerShotState] instance.
*/
fun Modifier.shimmerShot(shimmerState: ShimmerState): Modifier =
fun Modifier.shimmerShot(shimmerState: ShimmerShotState): Modifier =
composed {
val startOffsetX by animateFloatAsState(
targetValue = shimmerState.targetValue,
animationSpec = shimmerState.startOffsetXAnimationSpec,
animationSpec = shimmerState.animationSpec,
label = "shimmer"
)
drawWithContent {
Expand All @@ -43,26 +56,65 @@ fun Modifier.shimmerShot(shimmerState: ShimmerState): Modifier =
}
}

fun Modifier.shimmer(
isLoading: Boolean,
colors: List<Color> = DefaultColors,
durationMillis: Int = 2000,
easing: Easing = FastOutSlowInEasing
): Modifier =
if (isLoading) {
shimmer(colors, durationMillis, easing)
} else {
this
}

private fun Modifier.shimmer(
colors: List<Color>,
durationMillis: Int,
easing: Easing
): Modifier =
composed {
val transition = rememberInfiniteTransition(label = "")

val translateAnimation = transition.animateFloat(
initialValue = INITIAL_VALUE,
targetValue = TARGET_VALUE,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = easing
),
repeatMode = RepeatMode.Restart,
),
label = "Shimmer loading animation",
)

drawWithContent {
val width = size.width
val height = size.height
val offset = translateAnimation.value * width

drawContent()
val brush = Brush.linearGradient(
colors = colors,
start = Offset(offset, 0f),
end = Offset(offset + width, height)
)
drawRect(brush)
}
}

@Stable
class ShimmerState(
val colors: List<Color> = listOf(
Color.Transparent,
Color.White.copy(alpha = 0.7f),
Color.Transparent
),
class ShimmerShotState(
val colors: List<Color> = DefaultColors,
durationMillis: Int = 1200,
easing: Easing = FastOutSlowInEasing
) {

companion object {
private const val INITIAL_VALUE = -2f
private const val TARGET_VALUE = 2f
}

var targetValue: Float by mutableStateOf(INITIAL_VALUE)
private set

val startOffsetXAnimationSpec: AnimationSpec<Float> = tween(
val animationSpec: DurationBasedAnimationSpec<Float> = tween(
durationMillis = durationMillis,
easing = easing
)
Expand Down
Loading

0 comments on commit 8d5653d

Please sign in to comment.