Skip to content

Release 1.61 #1072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5d2612d
Merge remote-tracking branch 'origin/main' into develop
invalid-email-address Jun 5, 2024
310368a
Set version number to 1.61
invalid-email-address Jun 5, 2024
acfbca1
Bump build number
invalid-email-address Jun 5, 2024
711ba73
Merge remote-tracking branch 'origin/main' into develop
invalid-email-address Jun 5, 2024
50ca595
Merge branch 'main' into release/1.61
XanderZhu Jun 5, 2024
89ddb0d
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 5, 2024
d9bf364
Bump build number
invalid-email-address Jun 5, 2024
0a8bd18
iOS, shared improve solving step behavior (#1077)
ivan-magda Jun 7, 2024
f4e4930
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 7, 2024
8134be0
Bump build number
invalid-email-address Jun 7, 2024
05812c9
Android improve step solving behaviour (#1079)
XanderZhu Jun 12, 2024
2581ce4
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 12, 2024
33d001d
Android: Bump build number
invalid-email-address Jun 12, 2024
1cb21f8
ALTAPPS-1269: iOS implement scrollToCallToActionButton
ivan-magda Jun 17, 2024
7326b4c
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 17, 2024
b997b7e
iOS: Bump build number
invalid-email-address Jun 17, 2024
7eace4f
ALTAPPS-1280: iOS rename tracks to courses in AppStore description
ivan-magda Jun 18, 2024
7851bb0
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 18, 2024
36afff0
iOS: Bump build number
invalid-email-address Jun 18, 2024
1d2f634
Bundler: bump fastlane from 2.220.0 to 2.221.0 in /androidHyperskillA…
dependabot[bot] Jun 18, 2024
a4d4781
Bundler: bump fastlane from 2.220.0 to 2.221.0 in /iosHyperskillApp (…
dependabot[bot] Jun 18, 2024
f7e40e5
Merge remote-tracking branch 'origin/develop' into release/1.61
ivan-magda Jun 18, 2024
5ec2ef9
Bump build number
ivan-magda Jun 18, 2024
a51bf61
Send analytical event after feature cancel (#1078)
XanderZhu Jun 18, 2024
4517fe6
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 18, 2024
b90dd72
Bump build number
invalid-email-address Jun 18, 2024
8d5653d
Shared, Android: New first session onboarding (#1080)
XanderZhu Jun 19, 2024
906fb79
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 19, 2024
f98c42f
Bump build number
invalid-email-address Jun 19, 2024
b0d289b
iOS: Improve first session onboarding (#1086)
ivan-magda Jun 21, 2024
7344499
Bundler: Bump fastlane from 2.221.0 to 2.221.1 in /iosHyperskillApp (…
dependabot[bot] Jun 21, 2024
4001f25
Bundler: Bump fastlane from 2.221.0 to 2.221.1 in /androidHyperskillA…
dependabot[bot] Jun 21, 2024
f52e002
Merge remote-tracking branch 'origin/develop' into release/1.61
ivan-magda Jun 21, 2024
70c4e53
Bump build number
ivan-magda Jun 21, 2024
0a5fbcf
Android update notification onboarding screen (#1085)
XanderZhu Jun 21, 2024
5cbdbe3
Merge remote-tracking branch 'origin/develop' into release/1.61
invalid-email-address Jun 21, 2024
7fe5d33
Bump build number
invalid-email-address Jun 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion androidHyperskillApp/Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source "https://rubygems.org"
ruby "3.3.0"

gem "fastlane", "2.220.0"
gem "fastlane", "2.221.1"

eval_gemfile("fastlane/Pluginfile")
26 changes: 13 additions & 13 deletions androidHyperskillApp/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.939.0)
aws-sdk-core (3.196.1)
aws-partitions (1.946.0)
aws-sdk-core (3.197.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.82.0)
aws-sdk-core (~> 3, >= 3.193.0)
aws-sdk-kms (1.85.0)
aws-sdk-core (~> 3, >= 3.197.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.151.0)
aws-sdk-core (~> 3, >= 3.194.0)
aws-sdk-s3 (1.152.3)
aws-sdk-core (~> 3, >= 3.197.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0)
Expand Down Expand Up @@ -68,7 +68,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.220.0)
fastlane (2.221.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
Expand Down Expand Up @@ -159,9 +159,9 @@ GEM
httpclient (2.8.3)
jmespath (1.6.2)
json (2.7.2)
jwt (2.8.1)
jwt (2.8.2)
base64
mini_magick (4.12.0)
mini_magick (4.13.1)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
Expand All @@ -171,15 +171,15 @@ GEM
optparse (0.5.0)
os (1.1.4)
plist (3.7.1)
public_suffix (5.0.5)
public_suffix (5.1.1)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.8)
strscan (>= 3.0.9)
rexml (3.2.9)
strscan
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
Expand Down Expand Up @@ -224,7 +224,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
fastlane (= 2.220.0)
fastlane (= 2.221.1)
fastlane-plugin-firebase_app_distribution

RUBY VERSION
Expand Down
2 changes: 1 addition & 1 deletion androidHyperskillApp/fastlane/Fastfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fastlane_version "2.220.0"
fastlane_version "2.221.0"

default_platform(:android)

Expand Down
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
@@ -0,0 +1,23 @@
package org.hyperskill.app.android.core.extensions

import android.view.HapticFeedbackConstants
import android.view.View
import androidx.core.view.HapticFeedbackConstantsCompat

fun View.performRejectHapticFeedback(flag: Int = 0): Boolean =
performHapticFeedback(HapticFeedbackConstantsCompat.REJECT, flag = flag)

fun View.performConfirmHapticFeedback(flag: Int = 0): Boolean =
performHapticFeedback(HapticFeedbackConstantsCompat.CONFIRM, flag = flag)

// If REJECT feedback was not performed, then use default to haptic feedback
@Suppress("DEPRECATION")
fun View.performHapticFeedback(
feedbackConstant: Int,
flag: Int = 0,
defaultFeedbackConstant: Int = HapticFeedbackConstantsCompat.LONG_PRESS
): Boolean =
performHapticFeedback(
/* feedbackConstant = */ feedbackConstant,
/* flags = */ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING or flag
) || performHapticFeedback(defaultFeedbackConstant)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.hyperskill.app.android.core.extensions

import android.view.View
import android.view.ViewGroup
import androidx.core.widget.NestedScrollView

private const val DEFAULT_SMOOTH_SCROLL_DURATION = 250

fun NestedScrollView.smoothScrollToBottom(durationMilliseconds: Int = DEFAULT_SMOOTH_SCROLL_DURATION) {
val scrollView = this
val childCount = scrollView.childCount
if (childCount > 0) {
val view: View = scrollView.getChildAt(childCount - 1)
val lp = view.layoutParams as ViewGroup.MarginLayoutParams
val y = view.bottom + lp.bottomMargin + scrollView.paddingBottom
scrollView.smoothScrollTo(0, y, durationMilliseconds)
}
}
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,30 @@ 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
)

object ShimmerDefaults {
const val InfiniteShimmerDurationMillis: Int = 2000
const val ShimmerShotDurationMillis: Int = 1200
}

/**
* 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 +61,73 @@ fun Modifier.shimmerShot(shimmerState: ShimmerState): Modifier =
}
}

@Stable
class ShimmerState(
val colors: List<Color> = listOf(
Color.Transparent,
Color.White.copy(alpha = 0.7f),
Color.Transparent
),
durationMillis: Int = 1200,
fun Modifier.infiniteShimmer(
play: Boolean,
colors: List<Color> = DefaultColors,
durationMillis: Int = ShimmerDefaults.InfiniteShimmerDurationMillis,
delayMillis: Int = 0,
easing: Easing = FastOutSlowInEasing
) {
): Modifier =
if (play) {
infiniteShimmer(
colors = colors,
durationMillis = durationMillis,
delayMillis = delayMillis,
easing = easing
)
} else {
this
}

private fun Modifier.infiniteShimmer(
colors: List<Color>,
durationMillis: Int,
delayMillis: 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,
delayMillis = delayMillis.toInt()
),
repeatMode = RepeatMode.Restart,
),
label = "Shimmer loading animation",
)

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

companion object {
private const val INITIAL_VALUE = -2f
private const val TARGET_VALUE = 2f
drawContent()
val brush = Brush.linearGradient(
colors = colors,
start = Offset(offset, 0f),
end = Offset(offset + width, height)
)
drawRect(brush)
}
}

@Stable
class ShimmerShotState(
val colors: List<Color> = DefaultColors,
durationMillis: Int = ShimmerDefaults.ShimmerShotDurationMillis,
easing: Easing = FastOutSlowInEasing
) {

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
Loading